From dbfeef6a7d25b46f24bd840b907f0c66c76b0b54 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 19 Jan 2023 03:24:58 +0700 Subject: [PATCH 01/81] move scripts to a separate folder for better org --- script-opts/detect_image.conf | 17 -- script-opts/image_positioning.conf | 14 - script-opts/minimap.conf | 19 -- script-opts/mvi/1_detect_image.conf | 11 + script-opts/mvi/2_image_positioning.conf | 14 + script-opts/mvi/3_status_line.conf | 12 + script-opts/mvi/4_minimap.conf | 22 ++ script-opts/mvi/5_ruler.conf | 21 ++ script-opts/ruler.conf | 21 -- script-opts/status_line.conf | 14 - scripts/detect-image.lua | 73 ----- scripts/freeze-window.lua | 14 - scripts/image-positioning.lua | 277 ------------------- scripts/minimap.lua | 201 -------------- scripts/mvi/1_detect_image.lua | 72 +++++ scripts/mvi/2_image_positioning.lua | 310 +++++++++++++++++++++ scripts/mvi/3_status_line.lua | 138 ++++++++++ scripts/mvi/4_minimap.lua | 188 +++++++++++++ scripts/mvi/5_ruler.lua | 327 +++++++++++++++++++++++ scripts/mvi/6_freeze_window.lua | 15 ++ scripts/mvi/lib/std.lua | 46 ++++ scripts/mvi/main.lua | 16 ++ scripts/ruler.lua | 314 ---------------------- scripts/status-line.lua | 142 ---------- 24 files changed, 1192 insertions(+), 1106 deletions(-) delete mode 100644 script-opts/detect_image.conf delete mode 100644 script-opts/image_positioning.conf delete mode 100644 script-opts/minimap.conf create mode 100644 script-opts/mvi/1_detect_image.conf create mode 100644 script-opts/mvi/2_image_positioning.conf create mode 100644 script-opts/mvi/3_status_line.conf create mode 100644 script-opts/mvi/4_minimap.conf create mode 100644 script-opts/mvi/5_ruler.conf delete mode 100644 script-opts/ruler.conf delete mode 100644 script-opts/status_line.conf delete mode 100644 scripts/detect-image.lua delete mode 100644 scripts/freeze-window.lua delete mode 100644 scripts/image-positioning.lua delete mode 100644 scripts/minimap.lua create mode 100644 scripts/mvi/1_detect_image.lua create mode 100644 scripts/mvi/2_image_positioning.lua create mode 100644 scripts/mvi/3_status_line.lua create mode 100644 scripts/mvi/4_minimap.lua create mode 100644 scripts/mvi/5_ruler.lua create mode 100644 scripts/mvi/6_freeze_window.lua create mode 100644 scripts/mvi/lib/std.lua create mode 100644 scripts/mvi/main.lua delete mode 100644 scripts/ruler.lua delete mode 100644 scripts/status-line.lua diff --git a/script-opts/detect_image.conf b/script-opts/detect_image.conf deleted file mode 100644 index 586fb95..0000000 --- a/script-opts/detect_image.conf +++ /dev/null @@ -1,17 +0,0 @@ -# commands to execute when a file detected as an image (1 frame, no audio) is loaded or unloaded - -# an image was loaded, and the previous file was not an image (or there was no previous file) -command_on_first_image_loaded= -# an image was loaded (regardless of what the previous file was) -command_on_image_loaded= -# a non-image was loaded, and the previous file was an image -command_on_non_image_loaded= - -# the purpose of these "hooks" is to let you change bindings, profiles, reset properties... -# see https://mpv.io/manual/master/#list-of-input-commands for general command information -# note that there is no such thing as "unloading a profile", to emulate this you must create an opposite profile and load that - -# example possible values: -#command_on_first_image_loaded=apply-profile image; enable-section image-viewer; script-message status-line-enable -#command_on_image_loaded=no-osd set video-pan-x 0; script-message align-border "" -1 -#command_on_non_image_loaded=disable-section image-viewer; no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0; script-message status-line-disable diff --git a/script-opts/image_positioning.conf b/script-opts/image_positioning.conf deleted file mode 100644 index 998ec64..0000000 --- a/script-opts/image_positioning.conf +++ /dev/null @@ -1,14 +0,0 @@ -# size of the margins with drag-to-pan -drag_to_pan_margin=50 -drag_to_pan_move_if_full_view=no - -# size of the margins with pan-follows-cursor -pan_follows_cursor_margin=50 - -# size of the margins with cursor-centric-zoom -cursor_centric_zoom_margin=50 -# if the borders would show up, move the image -# this makes it not exactly cursor-centric in some cases -cursor_centric_zoom_auto_center=yes -# allow zooming out if the image can already fully fit in the window -cursor_centric_zoom_dezoom_if_full_view=no diff --git a/script-opts/minimap.conf b/script-opts/minimap.conf deleted file mode 100644 index 72a05ea..0000000 --- a/script-opts/minimap.conf +++ /dev/null @@ -1,19 +0,0 @@ -# whether to show by default -enabled=yes -# the position of the center of the minimap, in percentage of the window (x, y) -center=92,92 -# the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) -scale=12 -# the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) -max_size=16,16 -# opacity of the "image" (from 00=opaque to FF=transparent) -image_opacity=88 -# color of the "image" (#BBGGRR where each component rages from 00 to FF) -image_color=BBBBBB -# opacity of the "view" -view_opacity=BB -view_color=222222 -# whether the view should be drawn above the image -view_above_image=yes -# whether to show the minimap if the current image is fully visible -hide_when_full_image_in_view=yes diff --git a/script-opts/mvi/1_detect_image.conf b/script-opts/mvi/1_detect_image.conf new file mode 100644 index 0000000..cc73c8f --- /dev/null +++ b/script-opts/mvi/1_detect_image.conf @@ -0,0 +1,11 @@ +# MUST be 'key= value', no spaces before '=' or not loaded!!! +# commands to execute when a file detected as an image (1 frame, no audio) is loaded or unloaded +# these "hooks" let you change bindings, profiles, reset properties... mpv.io/manual/master/#list-of-input-commands for general command information +# CAN'T "unload a profile", to emulate this you must create an opposite profile and load that + +#on_load_image_first = loaded image, and the previous file was not an image (or there was no previous file) +#on_load_image = loaded image (regardless of what the previous file was) +#on_load_non_image = loaded non-image, and the previous file was an image +on_load_image_first= apply-profile mvi ; enable-section mvi; script-message status-line-enable +on_load_image= no-osd set video-pan-x 0; script-message align-border "" -1 +on_load_non_image= apply-profile mvi restore; disable-section mvi; no-osd set video-pan-y 0; no-osd set video-pan-x 0; no-osd set video-zoom 0; script-message status-line-disable diff --git a/script-opts/mvi/2_image_positioning.conf b/script-opts/mvi/2_image_positioning.conf new file mode 100644 index 0000000..7a47d71 --- /dev/null +++ b/script-opts/mvi/2_image_positioning.conf @@ -0,0 +1,14 @@ +drag_to_pan_margin=50 +drag_to_pan_move_if_full_view=no +#↑ size of the margins with drag-to-pan + +pan_follows_cursor_margin=50 +#↑ size of the margins with pan-follows-cursor + +cursor_centric_zoom_margin=50 +#↑ size of the margins with cursor-centric-zoom + +cursor_centric_zoom_auto_center=yes +#↑ if the borders would show up, move the image this makes it not exactly cursor-centric in some cases +cursor_centric_zoom_dezoom_if_full_view=no +#↑ allow zooming out if the image can already fully fit in the window diff --git a/script-opts/mvi/3_status_line.conf b/script-opts/mvi/3_status_line.conf new file mode 100644 index 0000000..2434f03 --- /dev/null +++ b/script-opts/mvi/3_status_line.conf @@ -0,0 +1,12 @@ +enabled=yes +#↑ whether to show by default +size=36 +#↑ its font size +margin=10 +#↑ distance of the text to the borders +text_top_left= +text_top_right= +text_bottom_left=${filename} [${playlist-pos-1}/${playlist-count}] +text_bottom_right=[${dwidth:X}x${dheight:X}] +# text to be expanded, see property expansion: https://mpv.io/manual/master/#property-expansion +# \N line breaks, can also use ass tags, see here: http://docs.aegisub.org/3.2/ASS_Tags/ diff --git a/script-opts/mvi/4_minimap.conf b/script-opts/mvi/4_minimap.conf new file mode 100644 index 0000000..d766f49 --- /dev/null +++ b/script-opts/mvi/4_minimap.conf @@ -0,0 +1,22 @@ +# boolean/numbers can't be converted with comments by github.com/mpv-player/mpv/blob/master/player/lua/options.lua +# other values also bug due to script imports not expecting comments +enabled=yes +#↑ whether to show by default +scale=12 +#↑ the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) +view_above_image=yes +#↑ whether the view should be drawn above the image +hide_when_full_image_in_view=yes +#↑ whether to show the minimap if the current image is fully visible +center=92,92 +#↑ the position of the center of the minimap, in percentage of the window (x, y) +max_size=16,16 +#↑ the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) +image_opacity=88 +#↑ opacity of the "image" (from 00=opaque to FF=transparent) +image_color=BBBBBB +#↑ color of the "image" (#BBGGRR where each component rages from 00 to FF) +view_opacity=BB +#↑ opacity of the "view" +view_color=222222 +#↑ diff --git a/script-opts/mvi/5_ruler.conf b/script-opts/mvi/5_ruler.conf new file mode 100644 index 0000000..66f8c9f --- /dev/null +++ b/script-opts/mvi/5_ruler.conf @@ -0,0 +1,21 @@ +show_distance=yes +#↑ whether to show the length of the lines between the two points +show_coordinates=yes +#↑ whether to show the coordinates of the two points +coordinates_space=image +#↑ the coordinate space of the text shown. Can be "image", "window", "both" +show_angles=degrees +#↑ can be "degrees", "radians", "both", or "no" +line_width=2 +dots_radius=3 +font_size=36 +line_color=33 +#↑ ranges from 00 (black) to FF (white) +confirm_bindings=MBTN_LEFT,ENTER +#↑ bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +exit_bindings=ESC +#↑ bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +set_first_point_on_begin=no +#↑ if yes, the first point will be immediately set at the cursor position when calling 'ruler' +clear_on_second_point_set=no +#↑ if yes, the ruler overlay will be immediately cleared when setting the second point diff --git a/script-opts/ruler.conf b/script-opts/ruler.conf deleted file mode 100644 index 5e6f48d..0000000 --- a/script-opts/ruler.conf +++ /dev/null @@ -1,21 +0,0 @@ -# whether to show the length of the lines between the two points -show_distance=yes -# whether to show the coordinates of the two points -show_coordinates=yes -# the coordinate space of the text shown. Can be "image", "window", "both" -coordinates_space=image -# can be "degrees", "radians", "both", or "no" -show_angles=degrees -line_width=2 -dots_radius=3 -font_size=36 -# ranges from 00 (black) to FF (white) -line_color=33 -# bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list -confirm_bindings=MBTN_LEFT,ENTER -# bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list -exit_bindings=ESC -# if yes, the first point will be immediately set at the cursor position when calling 'ruler' -set_first_point_on_begin=no -# if yes, the ruler overlay will be immediately cleared when setting the second point -clear_on_second_point_set=no diff --git a/script-opts/status_line.conf b/script-opts/status_line.conf deleted file mode 100644 index ec15108..0000000 --- a/script-opts/status_line.conf +++ /dev/null @@ -1,14 +0,0 @@ -# whether to show by default -enabled=yes -# its font size -size=36 -# distance of the text to the borders -margin=10 -# the text to be expanded -# see property expansion: https://mpv.io/manual/master/#property-expansion -# \N can be used for line breaks -# you can also use ass tags, see here: http://docs.aegisub.org/3.2/ASS_Tags/ -text_top_left= -text_top_right= -text_bottom_left=${filename} [${playlist-pos-1}/${playlist-count}] -text_bottom_right=[${dwidth:X}x${dheight:X}] diff --git a/scripts/detect-image.lua b/scripts/detect-image.lua deleted file mode 100644 index bfe52c2..0000000 --- a/scripts/detect-image.lua +++ /dev/null @@ -1,73 +0,0 @@ -local opts = { - command_on_first_image_loaded="", - command_on_image_loaded="", - command_on_non_image_loaded="", -} -local options = require 'mp.options' -local msg = require 'mp.msg' - -options.read_options(opts, nil, function() end) - -function run_maybe(str) - if str ~= "" then - mp.command(str) - end -end - -local was_image = false - -function set_image(is_image) - if is_image and not was_image then - msg.info("First image detected") - run_maybe(opts.command_on_first_image_loaded) - end - if is_image then - msg.info("Image detected") - run_maybe(opts.command_on_image_loaded) - end - if not is_image and was_image then - msg.info("Non-image detected") - run_maybe(opts.command_on_non_image_loaded) - end - was_image = is_image -end - -local properties = {} - -function properties_changed() - local dwidth = properties["dwidth"] - local tracks = properties["track-list"] - local path = properties["path"] - local framecount = properties["estimated-frame-count"] - - if not path or path == "" then return end - if not tracks or #tracks == 0 then return end - local audio_tracks = 0 - for _, track in ipairs(tracks) do - if track.type == "audio" then - audio_tracks = audio_tracks + 1 - end - end - - -- only do things when state is consistent - if not framecount and audio_tracks > 0 then - set_image(false) - elseif framecount and dwidth and dwidth > 0 then - -- png have 0 frames, jpg 1 ¯\_(ツ)_/¯ - set_image((framecount == 0 or framecount == 1) and audio_tracks == 0) - end -end - -function observe(propname) - mp.observe_property(propname, "native", function(_, val) - if val ~= properties[propname] then - properties[propname] = val - msg.verbose("Property " .. propname .. " changed") - properties_changed() - end - end) -end -observe("estimated-frame-count") -observe("track-list") -observe("dwidth") -observe("path") diff --git a/scripts/freeze-window.lua b/scripts/freeze-window.lua deleted file mode 100644 index 693c717..0000000 --- a/scripts/freeze-window.lua +++ /dev/null @@ -1,14 +0,0 @@ --- credit to TheAMM - -local size_changed = false - -mp.register_idle(function() - if not size_changed then return end - local ww, wh = mp.get_osd_size() - if not ww or ww <= 0 or not wh or wh <= 0 then return end - mp.set_property("geometry", string.format("%dx%d", ww, wh)) - size_changed = false -end) - -mp.observe_property("osd-width", "native", function() size_changed = true end) -mp.observe_property("osd-height", "native", function() size_changed = true end) diff --git a/scripts/image-positioning.lua b/scripts/image-positioning.lua deleted file mode 100644 index 6b03551..0000000 --- a/scripts/image-positioning.lua +++ /dev/null @@ -1,277 +0,0 @@ -local opts = { - drag_to_pan_margin = 50, - drag_to_pan_move_if_full_view=false, - - pan_follows_cursor_margin = 50, - - cursor_centric_zoom_margin = 50, - cursor_centric_zoom_auto_center = true, - cursor_centric_zoom_dezoom_if_full_view = false, -} -local options = require 'mp.options' -local msg = require 'mp.msg' -local assdraw = require 'mp.assdraw' - -options.read_options(opts, nil, function() end) - -function clamp(value, low, high) - if value <= low then - return low - elseif value >= high then - return high - else - return value - end -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 dim = mp.get_property_native("osd-dimensions") - if not dim then return end - 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 video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } - 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 dim.ml >= 0 and dim.mr >= 0 then - move_lateral = false - end - if dim.mt >= 0 and dim.mb >= 0 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_size[1] - if 2 * margin > dim.ml + dim.mr then - pX = clamp(pX, - (-margin + dim.w / 2) / video_size[1] - 0.5, - (margin - dim.w / 2) / video_size[1] + 0.5) - else - pX = clamp(pX, - (margin - dim.w / 2) / video_size[1] + 0.5, - (-margin + dim.w / 2) / video_size[1] - 0.5) - end - end - if move_up then - pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_size[2] - if 2 * margin > dim.mt + dim.mb then - pY = clamp(pY, - (-margin + dim.h / 2) / video_size[2] - 0.5, - (margin - dim.h / 2) / video_size[2] + 0.5) - else - pY = clamp(pY, - (margin - dim.h / 2) / video_size[2] + 0.5, - (-margin + dim.h / 2) / video_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 dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } - 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 / dim.w + 1, -1)) - local y = math.min(1, math.max(- 2 * mY / dim.h + 1, -1)) - local command = "" - local margin = opts.pan_follows_cursor_margin - if dim.ml + dim.mr < 0 then - command = command .. "no-osd set video-pan-x " .. clamp(x * (2 * margin - dim.ml - dim.mr) / (2 * video_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 dim.mt + dim.mb < 0 then - command = command .. "no-osd set video-pan-y " .. clamp(y * (2 * margin - dim.mt - dim.mb) / (2 * video_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 dim = mp.get_property_native("osd-dimensions") - if not dim then return end - - local margin = opts.cursor_centric_zoom_margin - - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } - - -- the size in pixels of the (in|de)crement - local diff_width = (2 ^ zoom_inc - 1) * video_size[1] - local diff_height = (2 ^ zoom_inc - 1) * video_size[2] - if not opts.cursor_centric_zoom_dezoom_if_full_view and - zoom_inc < 0 and - video_size[1] + diff_width + 2 * margin <= dim.w and - video_size[2] + diff_height + 2 * margin <= dim.h - then - -- the zoom decrement is too much, reduce it such that the full image is visible, no more, no less - -- in addition, this should take care of trying too zoom out while everything is already visible - local new_zoom_inc_x = math.log((dim.w - 2 * margin) / video_size[1]) / math.log(2) - local new_zoom_inc_y = math.log((dim.h - 2 * margin) / video_size[2]) / math.log(2) - local new_zoom_inc = math.min(0, math.min(new_zoom_inc_x, new_zoom_inc_y)) - zoom_inc = new_zoom_inc - diff_width = (2 ^ zoom_inc - 1) * video_size[1] - diff_height = (2 ^ zoom_inc - 1) * video_size[2] - end - local new_width = video_size[1] + diff_width - local new_height = video_size[2] + diff_height - - local mouse_pos_origin = {} - mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos() - local new_pan_x, new_pan_y - - -- some additional constraints: - -- if image can be fully visible (in either direction), set pan to 0 - -- if border would show on either side, then prefer adjusting the pan even if not cursor-centric - local auto_c = opts.cursor_centric_zoom_auto_center - if auto_c and video_size[1] + diff_width + 2 * margin <= dim.w then - new_pan_x = 0 - else - local pan_x = mp.get_property("video-pan-x") - local rx = (dim.ml + video_size[1] / 2 - mouse_pos_origin[1]) / (video_size[1] / 2) - new_pan_x = (pan_x * video_size[1] + rx * diff_width / 2) / new_width - if auto_c then - new_pan_x = clamp(new_pan_x, (dim.w - 2 * margin) / (2 * new_width) - 0.5, - (dim.w - 2 * margin) / (2 * new_width) + 0.5) - end - end - - if auto_c and video_size[2] + diff_height + 2 * margin <= dim.h then - new_pan_y = 0 - else - local pan_y = mp.get_property("video-pan-y") - local ry = (dim.mt + video_size[2] / 2 - mouse_pos_origin[2]) / (video_size[2] / 2) - new_pan_y = (pan_y * video_size[2] + ry * diff_height / 2) / new_height - if auto_c then - new_pan_y = clamp(new_pan_y, (dim.h - 2 * margin) / (2 * new_height) - 0.5, - (dim.h - 2 * margin) / (2 * new_height) + 0.5) - end - end - - local zoom_origin = mp.get_property("video-zoom") - mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) -end - -function align_border(x, y) - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } - local x, y = tonumber(x), tonumber(y) - local command = "" - if x then - command = command .. "no-osd set video-pan-x " .. clamp(- x * (dim.ml + dim.mr) / (2 * video_size[1]), -3, 3) .. ";" - end - if y then - command = command .. "no-osd set video-pan-y " .. clamp(- y * (dim.mt + dim.mb) / (2 * video_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) - if image_constrained == "yes" then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local margin = - (axis == "x" and amount > 0) and dim.ml - or (axis == "x" and amount < 0) and dim.mr - or (amount > 0) and dim.mt - or (amount < 0) and dim.mb - local vid_size = (axis == "x") and (dim.w - dim.ml - dim.mr) or (dim.h - dim.mt - dim.mb) - local pixels_moved = math.abs(amount) * vid_size - -- the margin is already visible, no point going further - if margin >= 0 then - return - elseif margin + pixels_moved > 0 then - amount = -(math.abs(amount) / amount) * margin / vid_size - 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 dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local command = "" - if (dim.ml + dim.mr >= 0) then - command = command .. "no-osd set video-pan-x 0" .. ";" - end - if (dim.mt + dim.mb >= 0) 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/scripts/minimap.lua b/scripts/minimap.lua deleted file mode 100644 index f1b9a7d..0000000 --- a/scripts/minimap.lua +++ /dev/null @@ -1,201 +0,0 @@ -local opts = { - enabled = true, - center = "92,92", - scale = 12, - max_size = "16,16", - image_opacity = "88", - image_color = "BBBBBB", - view_opacity = "BB", - view_color = "222222", - view_above_image = true, - hide_when_full_image_in_view = true, -} - -local msg = require 'mp.msg' -local assdraw = require 'mp.assdraw' -local options = require 'mp.options' - -options.read_options(opts, nil, function(c) - if c["enabled"] then - if opts.enabled then - enable() - else - disable() - end - end - mark_stale() -end) - -function split_comma(input) - local ret = {} - for str in string.gmatch(input, "([^,]+)") do - ret[#ret + 1] = tonumber(str) - end - return ret -end - -local active = false -local refresh = true - -function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) -end - -function mark_stale() - refresh = true -end - -function refresh_minimap() - if not refresh then return end - refresh = false - - local dim = mp.get_property_native("osd-dimensions") - if not dim then - draw_ass("") - return - end - local ww, wh = dim.w, dim.h - - if not (ww > 0 and wh > 0) then return end - if opts.hide_when_full_image_in_view then - if dim.mt >= 0 and dim.mb >= 0 and dim.ml >= 0 and dim.mr >= 0 then - draw_ass("") - return - end - end - - local center = split_comma(opts.center) - center[1] = center[1] * 0.01 * ww - center[2] = center[2] * 0.01 * wh - local cutoff = split_comma(opts.max_size) - cutoff[1] = cutoff[1] * 0.01 * ww * 0.5 - cutoff[2] = cutoff[2] * 0.01 * wh * 0.5 - - local a = assdraw.ass_new() - local draw = function(x, y, w, h, opacity, color) - a:new_event() - a:pos(center[1], center[2]) - a:append("{\\bord0}") - a:append("{\\shad0}") - a:append("{\\c&" .. color .. "&}") - a:append("{\\2a&HFF}") - a:append("{\\3a&HFF}") - a:append("{\\4a&HFF}") - a:append("{\\1a&H" .. opacity .. "}") - w = w * 0.5 - h = h * 0.5 - a:draw_start() - local rounded = {true,true,true,true} -- tl, tr, br, bl - local x0,y0,x1,y1 = x-w, y-h, x+w, y+h - if x0 < -cutoff[1] then - x0 = -cutoff[1] - rounded[4] = false - rounded[1] = false - end - if y0 < -cutoff[2] then - y0 = -cutoff[2] - rounded[1] = false - rounded[2] = false - end - if x1 > cutoff[1] then - x1 = cutoff[1] - rounded[2] = false - rounded[3] = false - end - if y1 > cutoff[2] then - y1 = cutoff[2] - rounded[3] = false - rounded[4] = false - end - - local r = 3 - local c = 0.551915024494 * r - if rounded[0] then - a:move_to(x0 + r, y0) - else - a:move_to(x0,y0) - end - if rounded[1] then - a:line_to(x1 - r, y0) - a:bezier_curve(x1 - r + c, y0, x1, y0 + r - c, x1, y0 + r) - else - a:line_to(x1, y0) - end - if rounded[2] then - a:line_to(x1, y1 - r) - a:bezier_curve(x1, y1 - r + c, x1 - r + c, y1, x1 - r, y1) - else - a:line_to(x1, y1) - end - if rounded[3] then - a:line_to(x0 + r, y1) - a:bezier_curve(x0 + r - c, y1, x0, y1 - r + c, x0, y1 - r) - else - a:line_to(x0, y1) - end - if rounded[4] then - a:line_to(x0, y0 + r) - a:bezier_curve(x0, y0 + r - c, x0 + r - c, y0, x0 + r, y0) - else - a:line_to(x0, y0) - end - a:draw_stop() - end - local image = function() - draw((dim.ml/2 - dim.mr/2) / opts.scale, - (dim.mt/2 - dim.mb/2) / opts.scale, - (ww - dim.ml - dim.mr) / opts.scale, - (wh - dim.mt - dim.mb) / opts.scale, - opts.image_opacity, - opts.image_color) - end - local view = function() - draw(0, - 0, - ww / opts.scale, - wh / opts.scale, - opts.view_opacity, - opts.view_color) - end - if opts.view_above_image then - image() - view() - else - view() - image() - end - draw_ass(a.text) -end - -function enable() - if active then return end - active = true - mp.observe_property("osd-dimensions", nil, mark_stale) - mp.register_idle(refresh_minimap) - mark_stale() -end - -function disable() - if not active then return end - active = false - mp.unobserve_property(mark_stale) - mp.unregister_idle(refresh_minimap) - draw_ass("") -end - -function toggle() - if active then - disable() - else - enable() - end -end - -if opts.enabled then - enable() -end - -mp.add_key_binding(nil, "minimap-enable", enable) -mp.add_key_binding(nil, "minimap-disable", disable) -mp.add_key_binding(nil, "minimap-toggle", toggle) diff --git a/scripts/mvi/1_detect_image.lua b/scripts/mvi/1_detect_image.lua new file mode 100644 index 0000000..722d757 --- /dev/null +++ b/scripts/mvi/1_detect_image.lua @@ -0,0 +1,72 @@ +-- Detects when 🖼 are loaded and allows you to run commands configured in mvi_1_detect_image.conf + +local std = require "lib/std".std + +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_dir_base = std.basename(script_dir) -- mvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_file_name = std.filename(script_path) -- .lua +local script_stem = std.delua(script_file_name) -- +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ + +local opts = { + on_load_image_first = "", + on_load_image = "", + on_load_non_image = "", +} +local options = require 'mp.options' +local msg = require 'mp.msg' + +options.read_options(opts, opt_path_rel, function() end) +-- msg.info("on_load_image_first = " .. tostring(opts.on_load_image_first)) +-- msg.info("on_load_image = " .. tostring(opts.on_load_image)) +-- msg.info("on_load_non_image = " .. tostring(opts.on_load_non_image)) + +function run_maybe(str) + if str ~= "" then mp.command(str) end +end + +local wasImg = false +function set_image(isImg) + if isImg and not wasImg then msg.info("Detected 🖼 #1"); run_maybe(opts.on_load_image_first) end + if isImg then msg.info("Detected 🖼" ); run_maybe(opts.on_load_image ) end + if not isImg and wasImg then msg.info("Detected Non🖼"); run_maybe(opts.on_load_non_image ) end + wasImg = isImg +end + +local properties = {} +function properties_changed() + local framecount = properties["estimated-frame-count"] + local dwidth = properties["dwidth" ] + local tracks = properties["track-list" ] + local path = properties["path" ] + + if not path or path == "" then return end + if not tracks or #tracks == 0 then return end + local audio_tracks = 0 + for _, track in ipairs(tracks) do + if track.type == "audio" then + audio_tracks = audio_tracks + 1 + end + end + -- only do things when state is consistent + if not framecount and audio_tracks > 0 then set_image(false) + elseif framecount and dwidth and dwidth > 0 then set_image( + (framecount == 0 or framecount == 1) -- png have 0 frames, jpg 1 ¯\_(ツ)_/¯ + and audio_tracks == 0) + end +end + +function observe(propname) + mp.observe_property(propname, "native", function(_, val) + if val ~= properties[propname] then + properties[propname] = val + msg.verbose("Property " .. propname .. " changed") + properties_changed() + end + end) +end +observe("estimated-frame-count") +observe("track-list" ) +observe("dwidth" ) +observe("path" ) diff --git a/scripts/mvi/2_image_positioning.lua b/scripts/mvi/2_image_positioning.lua new file mode 100644 index 0000000..71f793b --- /dev/null +++ b/scripts/mvi/2_image_positioning.lua @@ -0,0 +1,310 @@ +-- Adds several high-level commands to zoom and pan: +-- `drag-to-pan` : pan the image with the cursor, while keeping the same part of the image under the cursor +-- `pan-follows-cursor` : pan the image in the direction of the cursor +-- `cursor-centric-zoom` : (de)zoom the video while keeping the same part of the image under the cursor +-- `align-border` : align the border of the image with the border of the window +-- `pan-image` : pan the image in a direction, optionally ignoring the zoom or forcing the image to stay visible +-- `rotate-video` : rotate the image in 90 degrees increment +-- `reset-pan-if-visible` : reset the pan if the entire image is visible + +local std = require "lib/std".std + +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_dir_base = std.basename(script_dir) -- mvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_file_name = std.filename(script_path) -- .lua +local script_stem = std.delua(script_file_name) -- +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ + +local opts = { + drag_to_pan_margin = 50, + drag_to_pan_move_if_full_view = false, + pan_follows_cursor_margin = 50, + cursor_centric_zoom_margin = 50, + cursor_centric_zoom_auto_center = true, + cursor_centric_zoom_dezoom_if_full_view = false, +} +local options = require 'mp.options' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +options.read_options(opts, opt_path_rel, function() end) +-- msg.info("drag_to_pan_margin = " .. tostring(opts.drag_to_pan_margin)) + +function clamp(value, low, high) + if value <= low then return low + elseif value >= high then return high + else return value end +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 dim = mp.get_property_native("osd-dimensions") + if not dim then return end + 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 video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + 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 dim.ml >= 0 and dim.mr >= 0 then + move_lateral = false + end + if dim.mt >= 0 and dim.mb >= 0 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_size[1] + if 2 * margin > dim.ml + dim.mr then + pX = clamp(pX, + (-margin + dim.w / 2) / video_size[1] - 0.5, + ( margin - dim.w / 2) / video_size[1] + 0.5) + else + pX = clamp(pX, + ( margin - dim.w / 2) / video_size[1] + 0.5, + (-margin + dim.w / 2) / video_size[1] - 0.5) + end + end + if move_up then + pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_size[2] + if 2 * margin > dim.mt + dim.mb then + pY = clamp(pY, + (-margin + dim.h / 2) / video_size[2] - 0.5, + ( margin - dim.h / 2) / video_size[2] + 0.5) + else + pY = clamp(pY, + ( margin - dim.h / 2) / video_size[2] + 0.5, + (-margin + dim.h / 2) / video_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 dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + 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 / dim.w + 1, -1)) + local y = math.min(1, math.max(- 2 * mY / dim.h + 1, -1)) + local command = "" + local margin = opts.pan_follows_cursor_margin + if dim.ml + dim.mr < 0 then + command = command .. "no-osd set video-pan-x " .. clamp(x * (2 * margin - dim.ml - dim.mr) / (2 * video_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 dim.mt + dim.mb < 0 then + command = command .. "no-osd set video-pan-y " .. clamp(y * (2 * margin - dim.mt - dim.mb) / (2 * video_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 dim = mp.get_property_native("osd-dimensions") + if not dim then return end + + local margin = opts.cursor_centric_zoom_margin + + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + + -- the size in pixels of the (in|de)crement + local diff_width = (2 ^ zoom_inc - 1) * video_size[1] + local diff_height = (2 ^ zoom_inc - 1) * video_size[2] + if not opts.cursor_centric_zoom_dezoom_if_full_view and + zoom_inc < 0 and + video_size[1] + diff_width + 2 * margin <= dim.w and + video_size[2] + diff_height + 2 * margin <= dim.h + then + -- the zoom decrement is too much, reduce it such that the full image is visible, no more, no less + -- in addition, this should take care of trying too zoom out while everything is already visible + local new_zoom_inc_x = math.log((dim.w - 2 * margin) / video_size[1]) / math.log(2) + local new_zoom_inc_y = math.log((dim.h - 2 * margin) / video_size[2]) / math.log(2) + local new_zoom_inc = math.min(0, math.min(new_zoom_inc_x, new_zoom_inc_y)) + zoom_inc = new_zoom_inc + diff_width = (2 ^ zoom_inc - 1) * video_size[1] + diff_height = (2 ^ zoom_inc - 1) * video_size[2] + end + local new_width = video_size[1] + diff_width + local new_height = video_size[2] + diff_height + + local mouse_pos_origin = {} + mouse_pos_origin[1], mouse_pos_origin[2] = mp.get_mouse_pos() + local new_pan_x, new_pan_y + + -- some additional constraints: + -- if image can be fully visible (in either direction), set pan to 0 + -- if border would show on either side, then prefer adjusting the pan even if not cursor-centric + local auto_c = opts.cursor_centric_zoom_auto_center + if auto_c and video_size[1] + diff_width + 2 * margin <= dim.w then + new_pan_x = 0 + else + local pan_x = mp.get_property("video-pan-x") + local rx = (dim.ml + video_size[1] / 2 - mouse_pos_origin[1]) / (video_size[1] / 2) + new_pan_x = (pan_x * video_size[1] + rx * diff_width / 2) / new_width + if auto_c then + new_pan_x = clamp(new_pan_x, (dim.w - 2 * margin) / (2 * new_width) - 0.5, - (dim.w - 2 * margin) / (2 * new_width) + 0.5) + end + end + + if auto_c and video_size[2] + diff_height + 2 * margin <= dim.h then + new_pan_y = 0 + else + local pan_y = mp.get_property("video-pan-y") + local ry = (dim.mt + video_size[2] / 2 - mouse_pos_origin[2]) / (video_size[2] / 2) + new_pan_y = (pan_y * video_size[2] + ry * diff_height / 2) / new_height + if auto_c then + new_pan_y = clamp(new_pan_y, (dim.h - 2 * margin) / (2 * new_height) - 0.5, - (dim.h - 2 * margin) / (2 * new_height) + 0.5) + end + end + + local zoom_origin = mp.get_property("video-zoom") + mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) +end + +function align_border(x, y) + -- local dim_s = mp.get_property("osd-dimensions"); print("dim_s"..tostring(dim_s)) + + local dim = mp.get_property_native("osd-dimensions") + local dimw = dim.w + -- if not dim.w then print("returning on zero dim.w=¦"..tostring(dim.w)); return; end + -- if not dim then return end + print("dimw=¦"..tostring(dimw).."¦") + if dimw==0 then + print("returning on zero dimw=¦"..dimw.."¦") + return + else + print("not returning no nonzero dimw=¦"..dimw.."¦") + end + print("dim=¦"..tostring(dim).."¦") + print("dim.w=¦"..tostring(dim.w).."¦") + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local x, y = tonumber(x), tonumber(y) + local command = "" + print("w=¦"..tostring(dim.w) .."¦".."ml=¦"..tostring(dim.ml) .."¦".."mr=¦"..tostring(dim.mr) .."¦".."h=¦"..tostring(dim.h) .."¦".."mt=¦"..tostring(dim.mt) .."¦".."mb=¦"..tostring(dim.mb) .."¦") + print("x=¦"..tostring(x) .. "¦y=¦"..tostring(y).."¦".."video_size=¦"..tostring(video_size[1]).."/"..tostring(video_size[2]).."¦") + if x then command = command .."no-osd set video-pan-x ".. clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]), -3, 3) .. ";" end + if y then command = command .."no-osd set video-pan-y ".. clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]), -3, 3) .. ";" end + print("cmd after y="..tostring(command)) + 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) + if image_constrained == "yes" then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local margin = + (axis == "x" and amount > 0) and dim.ml + or (axis == "x" and amount < 0) and dim.mr + or (amount > 0) and dim.mt + or (amount < 0) and dim.mb + local vid_size = (axis == "x") and (dim.w - dim.ml - dim.mr) or (dim.h - dim.mt - dim.mb) + local pixels_moved = math.abs(amount) * vid_size + -- the margin is already visible, no point going further + if margin >= 0 then + return + elseif margin + pixels_moved > 0 then + amount = -(math.abs(amount) / amount) * margin / vid_size + 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 dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local command = "" + if (dim.ml + dim.mr >= 0) then + command = command .. "no-osd set video-pan-x 0" .. ";" + end + if (dim.mt + dim.mb >= 0) then + command = command .. "no-osd set video-pan-y 0" .. ";" + end + if command ~= "" then + mp.command(command) + end +end + +function ttt(x, y) + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local x, y = tonumber(x), tonumber(y) + local command = "" + print("w=¦"..tostring(dim.w) .."¦".."ml=¦"..tostring(dim.ml) .."¦".."mr=¦"..tostring(dim.mr) .."¦".."h=¦"..tostring(dim.h) .."¦".."mt=¦"..tostring(dim.mt) .."¦".."mb=¦"..tostring(dim.mb) .."¦") + print("x=¦"..tostring(x) .. "¦y=¦"..tostring(y).."¦".."video_size=¦"..tostring(video_size[1]).."/"..tostring(video_size[2]).."¦") + if x then command = command .."no-osd set video-pan-x ".. clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]), -3, 3) .. ";" end + if y then command = command .."no-osd set video-pan-y ".. clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]), -3, 3) .. ";" end + print("cmd after y="..tostring(command)) +end +mp.add_key_binding(nil, "ttt" , ttt) + +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/scripts/mvi/3_status_line.lua b/scripts/mvi/3_status_line.lua new file mode 100644 index 0000000..c638b41 --- /dev/null +++ b/scripts/mvi/3_status_line.lua @@ -0,0 +1,138 @@ +-- Adds a status line that can show different properties in the corner of the window. By default it shows `filename [positon/total]` in the bottom left +-- Can be activated with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` and configured through mvi_3_status_line.conf + +local std = require "lib/std".std + +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_dir_base = std.basename(script_dir) -- mvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_file_name = std.filename(script_path) -- .lua +local script_stem = std.delua(script_file_name) -- +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ + +local opts = { + enabled = true, + size = 36, + margin = 10, + text_top_left = "", + text_top_right = "", + text_bottom_left = "${filename} [${playlist-pos-1}/${playlist-count}]", + text_bottom_right = "[${dwidth:X}x${dheight:X}]", +} + +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local options = require 'mp.options' + +options.read_options(opts, opt_path_rel, function(c) + if c["enabled"] then + if opts.enabled then enable() + else disable() end + end + if c["size"] or c["margin"] then + mark_stale() + end + if c["text_top_left" ] or + c["text_top_right" ] or + c["text_bottom_left" ] or + c["text_bottom_right"] + then + observe_properties() + mark_stale() + end +end) +-- msg.info("text_bottom_left = " .. tostring(opts.text_bottom_left)) + +local stale = true +local active = false + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + +function refresh() + if not stale then return end + stale = false + local a = assdraw:ass_new() + local draw_text = function(text, an, x, y) + if text == "" then return end + local expanded = mp.command_native({ "expand-text", text}) + if not expanded then + msg.error("Error expanding status-line") + return + end + msg.verbose("Status-line changed to: " .. expanded) + a:new_event() + a:an(an) + a:pos(x,y) + a:append("{\\fs".. opts.size.. "}{\\bord1.0}") + a:append(expanded) + end + local w,h = mp.get_osd_size() + local m = opts.margin + draw_text(opts.text_top_left , 7, m, m) + draw_text(opts.text_top_right , 9, w-m, m) + draw_text(opts.text_bottom_left , 1, m, h-m) + draw_text(opts.text_bottom_right , 3, w-m, h-m) + draw_ass(a.text) +end + +function mark_stale() + stale = true +end + +function observe_properties() + mp.unobserve_property(mark_stale) + if not active then return end + for _, str in ipairs({ + opts.text_top_left, + opts.text_top_right, + opts.text_bottom_left, + opts.text_bottom_right, + }) do + local start = 0 + while true do + local s, e, cap = string.find(str, "%${[?!]?([%l%d-/]*)", start) + if not s then break end + msg.verbose("Observing property " .. cap) + mp.observe_property(cap, nil, mark_stale) + start = e + end + end + mp.observe_property("osd-width" , nil, mark_stale) + mp.observe_property("osd-height", nil, mark_stale) +end + +function enable() + if active then return end + active = true + observe_properties() + mp.register_idle(refresh) + mark_stale() +end + + +function disable() + if not active then return end + active = false + observe_properties() + mp.unregister_idle(refresh) + draw_ass("") +end + +function toggle() + if active then + disable() + else + enable() + end +end + +if opts.enabled then + enable() +end + +mp.add_key_binding(nil, "status-line-enable" , enable) +mp.add_key_binding(nil, "status-line-disable" , disable) +mp.add_key_binding(nil, "status-line-toggle" , toggle) diff --git a/scripts/mvi/4_minimap.lua b/scripts/mvi/4_minimap.lua new file mode 100644 index 0000000..ad9b4e3 --- /dev/null +++ b/scripts/mvi/4_minimap.lua @@ -0,0 +1,188 @@ +-- Adds a minimap that displays the position of the image relative to the view +-- activated with `minimap-enable`, `minimap-disable`, `minimap-toggle` and configured via mvi_4_minimap.conf + +local std = require "lib/std".std + +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_dir_base = std.basename(script_dir) -- mvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_file_name = std.filename(script_path) -- .lua +local script_stem = std.delua(script_file_name) -- +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ + +local opts = { + enabled = true, + center = "92,92", + scale = 12, + max_size = "16,16", + image_opacity = "88", + image_color = "BBBBBB", + view_opacity = "BB", + view_color = "222222", + view_above_image = true, + hide_when_full_image_in_view = true, +} + +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local options = require 'mp.options' + +options.read_options(opts, opt_path_rel, function(c) + if c["enabled"] then + if opts.enabled then enable() + else disable() end + end + mark_stale() +end) + +function split_comma(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = tonumber(str) + end + return ret +end +-- msg.info("center = " .. tostring(opts.center)) + +local active = false +local refresh = true + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + +function mark_stale() + refresh = true +end + +function refresh_minimap() + if not refresh then return end + refresh = false + + local dim = mp.get_property_native("osd-dimensions") + if not dim then + draw_ass("") + return + end + local ww, wh = dim.w, dim.h + + if not (ww > 0 and wh > 0) then return end + if opts.hide_when_full_image_in_view then + if dim.mt >= 0 and dim.mb >= 0 and dim.ml >= 0 and dim.mr >= 0 then + draw_ass("") + return + end + end + + local center = split_comma(opts.center) + center[1] = center[1] * 0.01 * ww + center[2] = center[2] * 0.01 * wh + local cutoff = split_comma(opts.max_size) + cutoff[1] = cutoff[1] * 0.01 * ww * 0.5 + cutoff[2] = cutoff[2] * 0.01 * wh * 0.5 + + local a = assdraw.ass_new() + local draw = function(x, y, w, h, opacity, color) + a:new_event() + a:pos(center[1], center[2]) + a:append("{\\bord0}") + a:append("{\\shad0}") + a:append("{\\c&" .. color .. "&}") + a:append("{\\2a&HFF}") + a:append("{\\3a&HFF}") + a:append("{\\4a&HFF}") + a:append("{\\1a&H" .. opacity .. "}") + w = w * 0.5 + h = h * 0.5 + a:draw_start() + local rounded = {true,true,true,true} -- tl, tr, br, bl + local x0,y0,x1,y1 = x-w, y-h, x+w, y+h + if x0 < -cutoff[1] then + x0 = -cutoff[1] + rounded[4] = false + rounded[1] = false + end + if y0 < -cutoff[2] then + y0 = -cutoff[2] + rounded[1] = false + rounded[2] = false + end + if x1 > cutoff[1] then + x1 = cutoff[1] + rounded[2] = false + rounded[3] = false + end + if y1 > cutoff[2] then + y1 = cutoff[2] + rounded[3] = false + rounded[4] = false + end + + local r = 3 + local c = 0.551915024494 * r + if rounded[0] then + a:move_to(x0 + r, y0 ) + else a:move_to(x0 , y0 ) end + if rounded[1] then + a:line_to(x1 - r, y0 ); a:bezier_curve(x1 - r + c, y0 , x1 , y0 + r - c, x1 , y0 + r) + else a:line_to(x1 , y0 ) end + if rounded[2] then + a:line_to(x1 , y1 - r); a:bezier_curve(x1 , y1 - r + c, x1 - r + c, y1 , x1 - r, y1 ) + else a:line_to(x1 , y1 ) end + if rounded[3] then + a:line_to(x0 + r, y1 ); a:bezier_curve(x0 + r - c, y1 , x0 , y1 - r + c, x0 , y1 - r) + else a:line_to(x0 , y1 ) end + if rounded[4] then + a:line_to(x0 , y0 + r); a:bezier_curve(x0 , y0 + r - c, x0 + r - c, y0 , x0 + r, y0 ) + else a:line_to(x0 , y0 ) end + a:draw_stop() + end + local image = function() + draw( + ( dim.ml/2 - dim.mr/2) / opts.scale, + ( dim.mt/2 - dim.mb/2) / opts.scale, + (ww - dim.ml - dim.mr ) / opts.scale, + (wh - dim.mt - dim.mb ) / opts.scale, + opts.image_opacity, + opts.image_color) + end + local view = function() + draw(0, + 0, + ww / opts.scale, + wh / opts.scale, + opts.view_opacity, + opts.view_color) + end + if opts.view_above_image then image(); view() + else view(); image() end + draw_ass(a.text) +end + +function enable() + if active then return end + active = true + mp.observe_property("osd-dimensions", nil, mark_stale) + mp.register_idle(refresh_minimap) + mark_stale() +end + +function disable() + if not active then return end + active = false + mp.unobserve_property(mark_stale) + mp.unregister_idle(refresh_minimap) + draw_ass("") +end + +function toggle() + if active then disable() + else enable(); end +end + +if opts.enabled then enable() end + +mp.add_key_binding(nil, "minimap-enable" , enable) +mp.add_key_binding(nil, "minimap-disable" , disable) +mp.add_key_binding(nil, "minimap-toggle" , toggle) diff --git a/scripts/mvi/5_ruler.lua b/scripts/mvi/5_ruler.lua new file mode 100644 index 0000000..82c2922 --- /dev/null +++ b/scripts/mvi/5_ruler.lua @@ -0,0 +1,327 @@ +-- Adds a `ruler` command that lets you measure positions, distances and angles in the image +-- Configure via mvi_5_ruler.conf + +local std = require "lib/std".std + +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_dir_base = std.basename(script_dir) -- mvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_file_name = std.filename(script_path) -- .lua +local script_stem = std.delua(script_file_name) -- +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ + +local opts = { + show_distance = true, + show_coordinates = true, + coordinates_space = "image", + show_angles = "degrees", + line_width = 2, + dots_radius = 3, + font_size = 36, + line_color = "33", + confirm_bindings = "MBTN_LEFT,ENTER", + exit_bindings = "ESC", + set_first_point_on_begin = false, + clear_on_second_point_set = false, +} + +local options = require 'mp.options' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' + +local state = 0 -- {0,1,2,3} = {inactive,setting first point,setting second point,done} +local first_point = nil -- in normalized video space coordinates +local second_point = nil -- in normalized video space coordinates +local video_dimensions_stale = false + +function split(input) + local ret = {} + for str in string.gmatch(input, "([^,]+)") do + ret[#ret + 1] = str + end + return ret +end + +local confirm_bindings = split(opts.confirm_bindings) +local exit_bindings = split(opts.exit_bindings) + +options.read_options(opts, opt_path_rel, function() + if state ~= 0 then + remove_bindings() + end + confirm_bindings = split(opts.confirm_bindings) + exit_bindings = split(opts.exit_bindings) + if state ~= 0 then + add_bindings() + mark_stale() + end +end) +-- msg.info("confirm_bindings = " .. tostring(opts.confirm_bindings)) + +function draw_ass(ass) + local ww, wh = mp.get_osd_size() + mp.set_osd_ass(ww, wh, ass) +end + + +function cursor_video_space_normalized(dim) + local mx, my = mp.get_mouse_pos() + local ret = {} + ret[1] = (mx - dim.ml) / (dim.w - dim.ml - dim.mr) + ret[2] = (my - dim.mt) / (dim.h - dim.mt - dim.mb) + return ret +end + +function refresh() + if not video_dimensions_stale then return end + video_dimensions_stale = false + + local dim = mp.get_property_native("osd-dimensions") + local out_params = mp.get_property_native("video-out-params") + if not dim or not out_params then + draw_ass("") + return + end + local vid_width = out_params.dw + local vid_height = out_params.dh + + function video_space_normalized_to_video(point) + local ret = {} + ret[1] = point[1] * vid_width + ret[2] = point[2] * vid_height + return ret + end + function video_space_normalized_to_screen(point) + local ret = {} + ret[1] = point[1] * (dim.w - dim.ml - dim.mr) + dim.ml + ret[2] = point[2] * (dim.h - dim.mt - dim.mb) + dim.mt + return ret + end + + local line_start = {} + local line_end = {} + if second_point then + line_start.image = video_space_normalized_to_video(first_point) + line_start.screen = video_space_normalized_to_screen(first_point) + line_end.image = video_space_normalized_to_video(second_point) + line_end.screen = video_space_normalized_to_screen(second_point) + elseif first_point then + line_start.image = video_space_normalized_to_video(first_point) + line_start.screen = video_space_normalized_to_screen(first_point) + line_end.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) + line_end.screen = {} + line_end.screen[1], line_end.screen[2] = mp.get_mouse_pos() + else + local mx, my = mp.get_mouse_pos() + line_start.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) + line_start.screen = {} + line_start.screen[1], line_start.screen[2] = mp.get_mouse_pos() + line_end = line_start + end + local distinct = (math.abs(line_start.screen[1] - line_end.screen[1]) >= 1 + or math.abs(line_start.screen[2] - line_end.screen[2]) >= 1) + + local a = assdraw:ass_new() + local draw_setup = function(bord) + a:new_event() + a:pos(0,0) + a:append("{\\bord" .. bord .. "}") + a:append("{\\shad0}") + local r = opts.line_color + a:append("{\\3c&H".. r .. r .. r .. "&}") + a:append("{\\1a&HFF}") + a:append("{\\2a&HFF}") + a:append("{\\3a&H00}") + a:append("{\\4a&HFF}") + a:draw_start() + end + local dot = function(pos, size) + draw_setup(size) + a:move_to(pos[1], pos[2]-0.5) + a:line_to(pos[1], pos[2]+0.5) + end + local line = function(from, to, size) + draw_setup(size) + a:move_to(from[1], from[2]) + a:line_to(to[1], to[2]) + end + if distinct then + dot(line_start.screen , opts.dots_radius) + line(line_start.screen , line_end.screen, opts.line_width) + dot(line_end.screen , opts.dots_radius) + else + dot(line_start.screen, opts.dots_radius) + end + + local line_info = function() + if not opts.show_distance then return end + a:new_event() + a:append("{\\fs36}{\\bord1}") + a:pos((line_start.screen[1] + line_end.screen[1]) / 2, (line_start.screen[2] + line_end.screen[2]) / 2) + local an = 1 + if line_start.image[1] < line_end.image[1] then an = an + 2 end + if line_start.image[2] < line_end.image[2] then an = an + 6 end + a:an(an) + local image = math.sqrt(math.pow(line_start.image[1] - line_end.image[1], 2) + math.pow(line_start.image[2] - line_end.image[2], 2)) + local screen = math.sqrt(math.pow(line_start.screen[1] - line_end.screen[1], 2) + math.pow(line_start.screen[2] - line_end.screen[2], 2)) + if opts.coordinates_space == "both" then + a:append(string.format("image: %.1f\\Nscreen: %.1f", image, screen)) + elseif opts.coordinates_space == "image" then + a:append(string.format("%.1f", image)) + elseif opts.coordinates_space == "window" then + a:append(string.format("%.1f", screen)) + end + end + local dot_info = function(pos, opposite) + if not opts.show_coordinates then return end + a:new_event() + a:append("{\\fs" .. opts.font_size .."}{\\bord1}") + a:pos(pos.screen[1], pos.screen[2]) + local an + if distinct then + an = 1 + if line_start.image[1] > line_end.image[1] then an = an + 2 end + if line_start.image[2] < line_end.image[2] then an = an + 6 end + else + an = 7 + end + if opposite then + an = 9 + 1 - an + end + a:an(an) + if opts.coordinates_space == "both" then + a:append(string.format("image: %.1f, %.1f\\Nscreen: %i, %i", + pos.image[1], pos.image[2], pos.screen[1], pos.screen[2])) + elseif opts.coordinates_space == "image" then + a:append(string.format("%.1f, %.1f", pos.image[1], pos.image[2])) + elseif opts.coordinates_space == "window" then + a:append(string.format("%i, %i", pos.screen[1], pos.screen[2])) + end + end + dot_info(line_start, true) + if distinct then + line_info() + dot_info(line_end, false) + end + if distinct and opts.show_angles ~= "no" then + local dist = 50 + local pos_from_angle = function(mult, angle) + return { + line_start.screen[1] + mult * dist * math.cos(angle), + line_start.screen[2] + mult * dist * math.sin(angle), + } + end + local extended = { line_start.screen[1], line_start.screen[2] } + if line_end.screen[1] > line_start.screen[1] then + extended[1] = extended[1] + dist + else + extended[1] = extended[1] - dist + end + line(line_start.screen, extended, math.max(0, opts.line_width-0.5)) + local angle = math.atan(math.abs(line_start.image[2] - line_end.image[2]) / math.abs(line_start.image[1] - line_end.image[1])) + local fix_angle + local an + if line_end.image[2] < line_start.image[2] and line_end.image[1] > line_start.image[1] then + -- upper-right + an = 4 + fix_angle = function(angle) return - angle end + elseif line_end.image[2] < line_start.image[2] then + -- upper-left + an = 6 + fix_angle = function(angle) return math.pi + angle end + elseif line_end.image[1] < line_start.image[1] then + -- bottom-left + an = 6 + fix_angle = function(angle) return math.pi - angle end + else + -- bottom-right + an = 4 + fix_angle = function(angle) return angle end + end + -- should implement this https://math.stackexchange.com/questions/873224/calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle + local cp1 = pos_from_angle(1, fix_angle(angle*1/4)) + local cp2 = pos_from_angle(1, fix_angle(angle*3/4)) + local p2 = pos_from_angle(1, fix_angle(angle)) + a:bezier_curve(cp1[1], cp1[2], cp2[1], cp2[2], p2[1], p2[2]) + + a:new_event() + a:append("{\\fs" .. opts.font_size .."}{\\bord1}") + local text_pos = pos_from_angle(1.1, fix_angle(angle*2/3)) -- you'd think /2 would make more sense, but *2/3 looks better + a:pos(text_pos[1], text_pos[2]) + a:an(an) + if opts.show_angles == "both" then + a:append(string.format("%.2f\\N%.1f°", angle, angle / math.pi * 180)) + elseif opts.show_angles == "degrees" then + a:append(string.format("%.1f°", angle / math.pi * 180)) + elseif opts.show_angles == "radians" then + a:append(string.format("%.2f", angle)) + end + end + + draw_ass(a.text) +end + +function mark_stale() + video_dimensions_stale = true +end + +function add_bindings() + mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) + for _, key in ipairs(confirm_bindings) do + mp.add_forced_key_binding(key, "ruler-next-" .. key, next_step) + end + for _, key in ipairs(exit_bindings) do + mp.add_forced_key_binding(key, "ruler-stop-" .. key, stop) + end +end + +function remove_bindings() + for _, key in ipairs(confirm_bindings) do + mp.remove_key_binding("ruler-next-" .. key) + end + for _, key in ipairs(exit_bindings) do + mp.remove_key_binding("ruler-stop-" .. key) + end + mp.remove_key_binding("ruler-mouse-move") +end + +function next_step() + if state == 0 then + state = 1 + mp.register_idle(refresh) + mp.observe_property("osd-dimensions", nil, mark_stale) + mark_stale() + add_bindings() + if opts.set_first_point_on_begin then + next_step() + end + elseif state == 1 then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + state = 2 + first_point = cursor_video_space_normalized(dim) + elseif state == 2 then + local dim = mp.get_property_native("osd-dimensions") + if not dim then return end + state = 3 + second_point = cursor_video_space_normalized(dim) + if opts.clear_on_second_point_set then + next_step() + end + else + stop() + end +end + +function stop() + if state == 0 then return end + mp.unregister_idle(refresh) + mp.unobserve_property(mark_stale) + remove_bindings() + state = 0 + first_point = nil + second_point = nil + draw_ass("") +end + +mp.add_key_binding(nil, "ruler", next_step) diff --git a/scripts/mvi/6_freeze_window.lua b/scripts/mvi/6_freeze_window.lua new file mode 100644 index 0000000..75eb5ca --- /dev/null +++ b/scripts/mvi/6_freeze_window.lua @@ -0,0 +1,15 @@ +-- By default, mpv automatically resizes the window when the current file changes to fit its size. This script freezes the window so that this does not happen -- credit to TheAMM + +local size_changed = false + +mp.register_idle(function() + if not size_changed then return end + local ww, wh = mp.get_osd_size() + if not ww or ww <= 0 or + not wh or wh <= 0 then return end + mp.set_property("geometry", string.format("%dx%d", ww, wh)) + size_changed = false +end) + +mp.observe_property("osd-width" , "native", function() size_changed = true end) +mp.observe_property("osd-height", "native", function() size_changed = true end) diff --git a/scripts/mvi/lib/std.lua b/scripts/mvi/lib/std.lua new file mode 100644 index 0000000..b00a6e9 --- /dev/null +++ b/scripts/mvi/lib/std.lua @@ -0,0 +1,46 @@ +local std = {} -- stdlib + +string.startswith = function(self, str) return self:find( '^' .. str ) ~= nil end +string.endswith = function(self, str) return self:find( str .. '$') ~= nil end +string.trim = function(self ) return self:match("^%s*(.-)%s*$" ) end + +function std.getScriptFullPath() + local source = debug.getinfo(2,"S").source + if source:sub(1,1) == "@" then + local path_arg = source:sub(2) + -- ↓ os-dependent resolution of relative paths, but we pass absolute paths + -- local fullpath = io.popen("realpath '"..path_arg.."'",'r'):read('a') + -- fullpath = fullpath:gsub('[\n\r]*$','') + return path_arg + else error("Caller was not defined in a file", 2) end +end +function std.dir_filename(fullpath) + if type(fullpath) ~= "string" then return nil end + local dirname, filename = fullpath:match('^(.*[/\\])([^/\\]-)$') + dirname = dirname or '' + filename = filename or fullpath + return dirname, filename +end +function std.dir( fullpath) + if type(fullpath) ~= "string" then return nil end + local dirname, filename = fullpath:match('^(.*[/\\])([^/\\]-)$') + return dirname or '' +end +function std.filename( fullpath) + if type(fullpath) ~= "string" then return nil end + local dirname, filename = fullpath:match('^(.*[/\\])([^/\\]-)$') + return filename or fullpath +end +function std.delua(filename) -- strip script's extension + if type(filename) ~= "string" then return nil end + return filename:gsub("(.*)(.lua)" ,"%1") -- (file)(.lua) → (file) +end + +function std.basename(path) -- get filename from path + if type(path) ~= "string" then return nil end + return path:gsub("(.*[/\\])(.*)" ,"%2") -- (path/)(file) → (file) +end + +return { + std = std, +} diff --git a/scripts/mvi/main.lua b/scripts/mvi/main.lua new file mode 100644 index 0000000..93e9ed4 --- /dev/null +++ b/scripts/mvi/main.lua @@ -0,0 +1,16 @@ +local std = require "lib/std".std + +local script_directory = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +function load(relative_path) + if relative_path:endswith('.lua') then relative_path = relative_path + else relative_path = relative_path .. ".lua" end + dofile(script_directory ..'/'.. relative_path) +end + +-- configs are @ script-opts/mvi/.conf +load('1_detect_image' ) -- detect when 🖼 are loaded, allows running commands from .conf +load('2_image_positioning' ) -- add several high-level commands to zoom and pan +load('3_status_line' ) -- add a status line that can show different properties in the window corner +load('4_minimap' ) -- add a minimap that displays the position of the image relative to the view +load('5_ruler' ) -- add a `ruler` command that lets you measure positions, distances and angles in the image +load('6_freeze_window' ) -- disabled window auto resizing on file changes to fit its size diff --git a/scripts/ruler.lua b/scripts/ruler.lua deleted file mode 100644 index 2cfcd0f..0000000 --- a/scripts/ruler.lua +++ /dev/null @@ -1,314 +0,0 @@ -local opts = { - show_distance = true, - show_coordinates = true, - coordinates_space = "image", - show_angles = "degrees", - line_width = 2, - dots_radius = 3, - font_size = 36, - line_color = "33", - confirm_bindings = "MBTN_LEFT,ENTER", - exit_bindings = "ESC", - set_first_point_on_begin = false, - clear_on_second_point_set = false, -} - -local options = require 'mp.options' -local msg = require 'mp.msg' -local assdraw = require 'mp.assdraw' - -local state = 0 -- {0,1,2,3} = {inactive,setting first point,setting second point,done} -local first_point = nil -- in normalized video space coordinates -local second_point = nil -- in normalized video space coordinates -local video_dimensions_stale = false - -function split(input) - local ret = {} - for str in string.gmatch(input, "([^,]+)") do - ret[#ret + 1] = str - end - return ret -end - -local confirm_bindings = split(opts.confirm_bindings) -local exit_bindings = split(opts.exit_bindings) - -options.read_options(opts, nil, function() - if state ~= 0 then - remove_bindings() - end - confirm_bindings = split(opts.confirm_bindings) - exit_bindings = split(opts.exit_bindings) - if state ~= 0 then - add_bindings() - mark_stale() - end -end) - -function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) -end - - -function cursor_video_space_normalized(dim) - local mx, my = mp.get_mouse_pos() - local ret = {} - ret[1] = (mx - dim.ml) / (dim.w - dim.ml - dim.mr) - ret[2] = (my - dim.mt) / (dim.h - dim.mt - dim.mb) - return ret -end - -function refresh() - if not video_dimensions_stale then return end - video_dimensions_stale = false - - local dim = mp.get_property_native("osd-dimensions") - local out_params = mp.get_property_native("video-out-params") - if not dim or not out_params then - draw_ass("") - return - end - local vid_width = out_params.dw - local vid_height = out_params.dh - - function video_space_normalized_to_video(point) - local ret = {} - ret[1] = point[1] * vid_width - ret[2] = point[2] * vid_height - return ret - end - function video_space_normalized_to_screen(point) - local ret = {} - ret[1] = point[1] * (dim.w - dim.ml - dim.mr) + dim.ml - ret[2] = point[2] * (dim.h - dim.mt - dim.mb) + dim.mt - return ret - end - - local line_start = {} - local line_end = {} - if second_point then - line_start.image = video_space_normalized_to_video(first_point) - line_start.screen = video_space_normalized_to_screen(first_point) - line_end.image = video_space_normalized_to_video(second_point) - line_end.screen = video_space_normalized_to_screen(second_point) - elseif first_point then - line_start.image = video_space_normalized_to_video(first_point) - line_start.screen = video_space_normalized_to_screen(first_point) - line_end.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) - line_end.screen = {} - line_end.screen[1], line_end.screen[2] = mp.get_mouse_pos() - else - local mx, my = mp.get_mouse_pos() - line_start.image = video_space_normalized_to_video(cursor_video_space_normalized(dim)) - line_start.screen = {} - line_start.screen[1], line_start.screen[2] = mp.get_mouse_pos() - line_end = line_start - end - local distinct = (math.abs(line_start.screen[1] - line_end.screen[1]) >= 1 - or math.abs(line_start.screen[2] - line_end.screen[2]) >= 1) - - local a = assdraw:ass_new() - local draw_setup = function(bord) - a:new_event() - a:pos(0,0) - a:append("{\\bord" .. bord .. "}") - a:append("{\\shad0}") - local r = opts.line_color - a:append("{\\3c&H".. r .. r .. r .. "&}") - a:append("{\\1a&HFF}") - a:append("{\\2a&HFF}") - a:append("{\\3a&H00}") - a:append("{\\4a&HFF}") - a:draw_start() - end - local dot = function(pos, size) - draw_setup(size) - a:move_to(pos[1], pos[2]-0.5) - a:line_to(pos[1], pos[2]+0.5) - end - local line = function(from, to, size) - draw_setup(size) - a:move_to(from[1], from[2]) - a:line_to(to[1], to[2]) - end - if distinct then - dot(line_start.screen, opts.dots_radius) - line(line_start.screen, line_end.screen, opts.line_width) - dot(line_end.screen, opts.dots_radius) - else - dot(line_start.screen, opts.dots_radius) - end - - local line_info = function() - if not opts.show_distance then return end - a:new_event() - a:append("{\\fs36}{\\bord1}") - a:pos((line_start.screen[1] + line_end.screen[1]) / 2, (line_start.screen[2] + line_end.screen[2]) / 2) - local an = 1 - if line_start.image[1] < line_end.image[1] then an = an + 2 end - if line_start.image[2] < line_end.image[2] then an = an + 6 end - a:an(an) - local image = math.sqrt(math.pow(line_start.image[1] - line_end.image[1], 2) + math.pow(line_start.image[2] - line_end.image[2], 2)) - local screen = math.sqrt(math.pow(line_start.screen[1] - line_end.screen[1], 2) + math.pow(line_start.screen[2] - line_end.screen[2], 2)) - if opts.coordinates_space == "both" then - a:append(string.format("image: %.1f\\Nscreen: %.1f", image, screen)) - elseif opts.coordinates_space == "image" then - a:append(string.format("%.1f", image)) - elseif opts.coordinates_space == "window" then - a:append(string.format("%.1f", screen)) - end - end - local dot_info = function(pos, opposite) - if not opts.show_coordinates then return end - a:new_event() - a:append("{\\fs" .. opts.font_size .."}{\\bord1}") - a:pos(pos.screen[1], pos.screen[2]) - local an - if distinct then - an = 1 - if line_start.image[1] > line_end.image[1] then an = an + 2 end - if line_start.image[2] < line_end.image[2] then an = an + 6 end - else - an = 7 - end - if opposite then - an = 9 + 1 - an - end - a:an(an) - if opts.coordinates_space == "both" then - a:append(string.format("image: %.1f, %.1f\\Nscreen: %i, %i", - pos.image[1], pos.image[2], pos.screen[1], pos.screen[2])) - elseif opts.coordinates_space == "image" then - a:append(string.format("%.1f, %.1f", pos.image[1], pos.image[2])) - elseif opts.coordinates_space == "window" then - a:append(string.format("%i, %i", pos.screen[1], pos.screen[2])) - end - end - dot_info(line_start, true) - if distinct then - line_info() - dot_info(line_end, false) - end - if distinct and opts.show_angles ~= "no" then - local dist = 50 - local pos_from_angle = function(mult, angle) - return { - line_start.screen[1] + mult * dist * math.cos(angle), - line_start.screen[2] + mult * dist * math.sin(angle), - } - end - local extended = { line_start.screen[1], line_start.screen[2] } - if line_end.screen[1] > line_start.screen[1] then - extended[1] = extended[1] + dist - else - extended[1] = extended[1] - dist - end - line(line_start.screen, extended, math.max(0, opts.line_width-0.5)) - local angle = math.atan(math.abs(line_start.image[2] - line_end.image[2]) / math.abs(line_start.image[1] - line_end.image[1])) - local fix_angle - local an - if line_end.image[2] < line_start.image[2] and line_end.image[1] > line_start.image[1] then - -- upper-right - an = 4 - fix_angle = function(angle) return - angle end - elseif line_end.image[2] < line_start.image[2] then - -- upper-left - an = 6 - fix_angle = function(angle) return math.pi + angle end - elseif line_end.image[1] < line_start.image[1] then - -- bottom-left - an = 6 - fix_angle = function(angle) return math.pi - angle end - else - -- bottom-right - an = 4 - fix_angle = function(angle) return angle end - end - -- should implement this https://math.stackexchange.com/questions/873224/calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle - local cp1 = pos_from_angle(1, fix_angle(angle*1/4)) - local cp2 = pos_from_angle(1, fix_angle(angle*3/4)) - local p2 = pos_from_angle(1, fix_angle(angle)) - a:bezier_curve(cp1[1], cp1[2], cp2[1], cp2[2], p2[1], p2[2]) - - a:new_event() - a:append("{\\fs" .. opts.font_size .."}{\\bord1}") - local text_pos = pos_from_angle(1.1, fix_angle(angle*2/3)) -- you'd think /2 would make more sense, but *2/3 looks better - a:pos(text_pos[1], text_pos[2]) - a:an(an) - if opts.show_angles == "both" then - a:append(string.format("%.2f\\N%.1f°", angle, angle / math.pi * 180)) - elseif opts.show_angles == "degrees" then - a:append(string.format("%.1f°", angle / math.pi * 180)) - elseif opts.show_angles == "radians" then - a:append(string.format("%.2f", angle)) - end - end - - draw_ass(a.text) -end - -function mark_stale() - video_dimensions_stale = true -end - -function add_bindings() - mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) - for _, key in ipairs(confirm_bindings) do - mp.add_forced_key_binding(key, "ruler-next-" .. key, next_step) - end - for _, key in ipairs(exit_bindings) do - mp.add_forced_key_binding(key, "ruler-stop-" .. key, stop) - end -end - -function remove_bindings() - for _, key in ipairs(confirm_bindings) do - mp.remove_key_binding("ruler-next-" .. key) - end - for _, key in ipairs(exit_bindings) do - mp.remove_key_binding("ruler-stop-" .. key) - end - mp.remove_key_binding("ruler-mouse-move") -end - -function next_step() - if state == 0 then - state = 1 - mp.register_idle(refresh) - mp.observe_property("osd-dimensions", nil, mark_stale) - mark_stale() - add_bindings() - if opts.set_first_point_on_begin then - next_step() - end - elseif state == 1 then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - state = 2 - first_point = cursor_video_space_normalized(dim) - elseif state == 2 then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - state = 3 - second_point = cursor_video_space_normalized(dim) - if opts.clear_on_second_point_set then - next_step() - end - else - stop() - end -end - -function stop() - if state == 0 then return end - mp.unregister_idle(refresh) - mp.unobserve_property(mark_stale) - remove_bindings() - state = 0 - first_point = nil - second_point = nil - draw_ass("") -end - -mp.add_key_binding(nil, "ruler", next_step) diff --git a/scripts/status-line.lua b/scripts/status-line.lua deleted file mode 100644 index 7ac5168..0000000 --- a/scripts/status-line.lua +++ /dev/null @@ -1,142 +0,0 @@ -local opts = { - enabled = true, - size = 36, - margin = 10, - text_top_left = "", - text_top_right = "", - text_bottom_left = "${filename} [${playlist-pos-1}/${playlist-count}]", - text_bottom_right = "[${dwidth:X}x${dheight:X}]", -} - -local msg = require 'mp.msg' -local assdraw = require 'mp.assdraw' -local options = require 'mp.options' - -options.read_options(opts, nil, function(c) - if c["enabled"] then - if opts.enabled then - enable() - else - disable() - end - end - if c["size"] or c["margin"] then - mark_stale() - end - if c["text_top_left"] or - c["text_top_right"] or - c["text_bottom_left"] or - c["text_bottom_right"] - then - observe_properties() - mark_stale() - end -end) - -local stale = true -local active = false - -function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) -end - -function refresh() - if not stale then return end - stale = false - local a = assdraw:ass_new() - local draw_text = function(text, an, x, y) - if text == "" then return end - local expanded = mp.command_native({ "expand-text", text}) - if not expanded then - msg.error("Error expanding status-line") - return - end - msg.verbose("Status-line changed to: " .. expanded) - a:new_event() - a:an(an) - a:pos(x,y) - a:append("{\\fs".. opts.size.. "}{\\bord1.0}") - a:append(expanded) - end - local w,h = mp.get_osd_size() - local m = opts.margin - draw_text(opts.text_top_left, 7, m, m) - draw_text(opts.text_top_right, 9, w-m, m) - draw_text(opts.text_bottom_left, 1, m, h-m) - draw_text(opts.text_bottom_right, 3, w-m, h-m) - draw_ass(a.text) -end - -function mark_stale() - stale = true -end - -function observe_properties() - mp.unobserve_property(mark_stale) - if not active then return end - for _, str in ipairs({ - opts.text_top_left, - opts.text_top_right, - opts.text_bottom_left, - opts.text_bottom_right, - }) do - local start = 0 - while true do - local s, e, cap = string.find(str, "%${[?!]?([%l%d-/]*)", start) - if not s then break end - msg.verbose("Observing property " .. cap) - mp.observe_property(cap, nil, mark_stale) - start = e - end - end - mp.observe_property("osd-width", nil, mark_stale) - mp.observe_property("osd-height", nil, mark_stale) -end - -function enable() - if active then return end - active = true - observe_properties() - mp.register_idle(refresh) - mark_stale() -end - - -function disable() - if not active then return end - active = false - observe_properties() - mp.unregister_idle(refresh) - draw_ass("") -end - -function toggle() - if active then - disable() - else - enable() - end -end - -if opts.enabled then - enable() -end - -mp.add_key_binding(nil, "status-line-enable", enable) -mp.add_key_binding(nil, "status-line-disable", disable) -mp.add_key_binding(nil, "status-line-toggle", toggle) - --- TODO remove -mp.add_key_binding(nil, "enable-status-line", function() - msg.warn("This binding is deprecated, use 'status-line-enable' instead") - enable() -end) -mp.add_key_binding(nil, "disable-status-line", function() - msg.warn("This binding is deprecated, use 'status-line-disable' instead") - disable() -end) -mp.add_key_binding(nil, "toggle-status-line", function() - msg.warn("This binding is deprecated, use 'status-line-toggle' instead") - toggle() -end) From c4a664ad6d41c5374986e1c17e1a0b22eadf3c2a Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 19 Jan 2023 04:20:07 +0700 Subject: [PATCH 02/81] :beetle: make all functions local to avoid conflicts --- scripts/mvi/1_detect_image.lua | 8 ++++---- scripts/mvi/2_image_positioning.lua | 16 ++++++++-------- scripts/mvi/3_status_line.lua | 21 +++++++++------------ scripts/mvi/4_minimap.lua | 14 +++++++------- scripts/mvi/5_ruler.lua | 18 +++++++++--------- 5 files changed, 37 insertions(+), 40 deletions(-) diff --git a/scripts/mvi/1_detect_image.lua b/scripts/mvi/1_detect_image.lua index 722d757..58ad866 100644 --- a/scripts/mvi/1_detect_image.lua +++ b/scripts/mvi/1_detect_image.lua @@ -22,12 +22,12 @@ options.read_options(opts, opt_path_rel, function() end) -- msg.info("on_load_image = " .. tostring(opts.on_load_image)) -- msg.info("on_load_non_image = " .. tostring(opts.on_load_non_image)) -function run_maybe(str) +local function run_maybe(str) if str ~= "" then mp.command(str) end end local wasImg = false -function set_image(isImg) +local function set_image(isImg) if isImg and not wasImg then msg.info("Detected 🖼 #1"); run_maybe(opts.on_load_image_first) end if isImg then msg.info("Detected 🖼" ); run_maybe(opts.on_load_image ) end if not isImg and wasImg then msg.info("Detected Non🖼"); run_maybe(opts.on_load_non_image ) end @@ -35,7 +35,7 @@ function set_image(isImg) end local properties = {} -function properties_changed() +local function properties_changed() local framecount = properties["estimated-frame-count"] local dwidth = properties["dwidth" ] local tracks = properties["track-list" ] @@ -57,7 +57,7 @@ function properties_changed() end end -function observe(propname) +local function observe(propname) mp.observe_property(propname, "native", function(_, val) if val ~= properties[propname] then properties[propname] = val diff --git a/scripts/mvi/2_image_positioning.lua b/scripts/mvi/2_image_positioning.lua index 71f793b..08d1271 100644 --- a/scripts/mvi/2_image_positioning.lua +++ b/scripts/mvi/2_image_positioning.lua @@ -31,7 +31,7 @@ local assdraw = require 'mp.assdraw' options.read_options(opts, opt_path_rel, function() end) -- msg.info("drag_to_pan_margin = " .. tostring(opts.drag_to_pan_margin)) -function clamp(value, low, high) +local function clamp(value, low, high) if value <= low then return low elseif value >= high then return high else return value end @@ -39,7 +39,7 @@ 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) +local function drag_to_pan_handler(table) if cleanup then cleanup(); cleanup = nil end if table["event"] == "down" then local dim = mp.get_property_native("osd-dimensions") @@ -104,7 +104,7 @@ function drag_to_pan_handler(table) end end -function pan_follows_cursor_handler(table) +local function pan_follows_cursor_handler(table) if cleanup then cleanup() cleanup = nil @@ -144,7 +144,7 @@ function pan_follows_cursor_handler(table) end end -function cursor_centric_zoom_handler(amt) +local function cursor_centric_zoom_handler(amt) local zoom_inc = tonumber(amt) if not zoom_inc or zoom_inc == 0 then return end local dim = mp.get_property_native("osd-dimensions") @@ -208,7 +208,7 @@ function cursor_centric_zoom_handler(amt) mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) end -function align_border(x, y) +local function align_border(x, y) -- local dim_s = mp.get_property("osd-dimensions"); print("dim_s"..tostring(dim_s)) local dim = mp.get_property_native("osd-dimensions") @@ -237,7 +237,7 @@ function align_border(x, y) end end -function pan_image(axis, amount, zoom_invariant, image_constrained) +local 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 @@ -265,13 +265,13 @@ function pan_image(axis, amount, zoom_invariant, image_constrained) mp.set_property_number(prop, old_pan + amount) end -function rotate_video(amt) +local 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 function reset_pan_if_visible() local dim = mp.get_property_native("osd-dimensions") if not dim then return end local command = "" diff --git a/scripts/mvi/3_status_line.lua b/scripts/mvi/3_status_line.lua index c638b41..bf0181a 100644 --- a/scripts/mvi/3_status_line.lua +++ b/scripts/mvi/3_status_line.lua @@ -46,12 +46,12 @@ end) local stale = true local active = false -function draw_ass(ass) +local function draw_ass(ass) local ww, wh = mp.get_osd_size() mp.set_osd_ass(ww, wh, ass) end -function refresh() +local function refresh() if not stale then return end stale = false local a = assdraw:ass_new() @@ -78,11 +78,11 @@ function refresh() draw_ass(a.text) end -function mark_stale() +local function mark_stale() stale = true end -function observe_properties() +local function observe_properties() mp.unobserve_property(mark_stale) if not active then return end for _, str in ipairs({ @@ -104,7 +104,7 @@ function observe_properties() mp.observe_property("osd-height", nil, mark_stale) end -function enable() +local function enable() if active then return end active = true observe_properties() @@ -113,7 +113,7 @@ function enable() end -function disable() +local function disable() if not active then return end active = false observe_properties() @@ -121,12 +121,9 @@ function disable() draw_ass("") end -function toggle() - if active then - disable() - else - enable() - end +local function toggle() + if active then disable() + else enable() end end if opts.enabled then diff --git a/scripts/mvi/4_minimap.lua b/scripts/mvi/4_minimap.lua index ad9b4e3..9e0469f 100644 --- a/scripts/mvi/4_minimap.lua +++ b/scripts/mvi/4_minimap.lua @@ -35,7 +35,7 @@ options.read_options(opts, opt_path_rel, function(c) mark_stale() end) -function split_comma(input) +local function split_comma(input) local ret = {} for str in string.gmatch(input, "([^,]+)") do ret[#ret + 1] = tonumber(str) @@ -47,16 +47,16 @@ end local active = false local refresh = true -function draw_ass(ass) +local function draw_ass(ass) local ww, wh = mp.get_osd_size() mp.set_osd_ass(ww, wh, ass) end -function mark_stale() +local function mark_stale() refresh = true end -function refresh_minimap() +local function refresh_minimap() if not refresh then return end refresh = false @@ -160,7 +160,7 @@ function refresh_minimap() draw_ass(a.text) end -function enable() +local function enable() if active then return end active = true mp.observe_property("osd-dimensions", nil, mark_stale) @@ -168,7 +168,7 @@ function enable() mark_stale() end -function disable() +local function disable() if not active then return end active = false mp.unobserve_property(mark_stale) @@ -176,7 +176,7 @@ function disable() draw_ass("") end -function toggle() +local function toggle() if active then disable() else enable(); end end diff --git a/scripts/mvi/5_ruler.lua b/scripts/mvi/5_ruler.lua index 82c2922..9788809 100644 --- a/scripts/mvi/5_ruler.lua +++ b/scripts/mvi/5_ruler.lua @@ -34,7 +34,7 @@ local first_point = nil -- in normalized video space coordinates local second_point = nil -- in normalized video space coordinates local video_dimensions_stale = false -function split(input) +local function split(input) local ret = {} for str in string.gmatch(input, "([^,]+)") do ret[#ret + 1] = str @@ -58,13 +58,13 @@ options.read_options(opts, opt_path_rel, function() end) -- msg.info("confirm_bindings = " .. tostring(opts.confirm_bindings)) -function draw_ass(ass) +local function draw_ass(ass) local ww, wh = mp.get_osd_size() mp.set_osd_ass(ww, wh, ass) end -function cursor_video_space_normalized(dim) +local function cursor_video_space_normalized(dim) local mx, my = mp.get_mouse_pos() local ret = {} ret[1] = (mx - dim.ml) / (dim.w - dim.ml - dim.mr) @@ -72,7 +72,7 @@ function cursor_video_space_normalized(dim) return ret end -function refresh() +local function refresh() if not video_dimensions_stale then return end video_dimensions_stale = false @@ -261,11 +261,11 @@ function refresh() draw_ass(a.text) end -function mark_stale() +local function mark_stale() video_dimensions_stale = true end -function add_bindings() +local function add_bindings() mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) for _, key in ipairs(confirm_bindings) do mp.add_forced_key_binding(key, "ruler-next-" .. key, next_step) @@ -275,7 +275,7 @@ function add_bindings() end end -function remove_bindings() +local function remove_bindings() for _, key in ipairs(confirm_bindings) do mp.remove_key_binding("ruler-next-" .. key) end @@ -285,7 +285,7 @@ function remove_bindings() mp.remove_key_binding("ruler-mouse-move") end -function next_step() +local function next_step() if state == 0 then state = 1 mp.register_idle(refresh) @@ -313,7 +313,7 @@ function next_step() end end -function stop() +local function stop() if state == 0 then return end mp.unregister_idle(refresh) mp.unobserve_property(mark_stale) From 7e012a08a47f7d567e6ea8c5fe4d8bb011e62178 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 19 Jan 2023 04:20:35 +0700 Subject: [PATCH 03/81] fix conf file names --- scripts/mvi/1_detect_image.lua | 2 +- scripts/mvi/3_status_line.lua | 2 +- scripts/mvi/4_minimap.lua | 2 +- scripts/mvi/5_ruler.lua | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mvi/1_detect_image.lua b/scripts/mvi/1_detect_image.lua index 58ad866..9676d87 100644 --- a/scripts/mvi/1_detect_image.lua +++ b/scripts/mvi/1_detect_image.lua @@ -1,4 +1,4 @@ --- Detects when 🖼 are loaded and allows you to run commands configured in mvi_1_detect_image.conf +-- Detects when 🖼 are loaded and allows you to run commands configured in 1_detect_image.conf local std = require "lib/std".std diff --git a/scripts/mvi/3_status_line.lua b/scripts/mvi/3_status_line.lua index bf0181a..3afa93c 100644 --- a/scripts/mvi/3_status_line.lua +++ b/scripts/mvi/3_status_line.lua @@ -1,5 +1,5 @@ -- Adds a status line that can show different properties in the corner of the window. By default it shows `filename [positon/total]` in the bottom left --- Can be activated with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` and configured through mvi_3_status_line.conf +-- Can be activated with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` and configured through 3_status_line.conf local std = require "lib/std".std diff --git a/scripts/mvi/4_minimap.lua b/scripts/mvi/4_minimap.lua index 9e0469f..d6d4d97 100644 --- a/scripts/mvi/4_minimap.lua +++ b/scripts/mvi/4_minimap.lua @@ -1,5 +1,5 @@ -- Adds a minimap that displays the position of the image relative to the view --- activated with `minimap-enable`, `minimap-disable`, `minimap-toggle` and configured via mvi_4_minimap.conf +-- activated with `minimap-enable`, `minimap-disable`, `minimap-toggle` and configured via 4_minimap.conf local std = require "lib/std".std diff --git a/scripts/mvi/5_ruler.lua b/scripts/mvi/5_ruler.lua index 9788809..33d5fba 100644 --- a/scripts/mvi/5_ruler.lua +++ b/scripts/mvi/5_ruler.lua @@ -1,5 +1,5 @@ -- Adds a `ruler` command that lets you measure positions, distances and angles in the image --- Configure via mvi_5_ruler.conf +-- Configure via 5_ruler.conf local std = require "lib/std".std From 4cdde179d52b5ab07fb77367dcfdd3f5ff5d411b Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 19 Jan 2023 04:20:57 +0700 Subject: [PATCH 04/81] reformat slightly --- scripts/mvi/1_detect_image.lua | 1 + scripts/mvi/3_status_line.lua | 10 ++++------ scripts/mvi/5_ruler.lua | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/scripts/mvi/1_detect_image.lua b/scripts/mvi/1_detect_image.lua index 9676d87..016dc7a 100644 --- a/scripts/mvi/1_detect_image.lua +++ b/scripts/mvi/1_detect_image.lua @@ -66,6 +66,7 @@ local function observe(propname) end end) end + observe("estimated-frame-count") observe("track-list" ) observe("dwidth" ) diff --git a/scripts/mvi/3_status_line.lua b/scripts/mvi/3_status_line.lua index 3afa93c..14c517e 100644 --- a/scripts/mvi/3_status_line.lua +++ b/scripts/mvi/3_status_line.lua @@ -57,7 +57,7 @@ local function refresh() local a = assdraw:ass_new() local draw_text = function(text, an, x, y) if text == "" then return end - local expanded = mp.command_native({ "expand-text", text}) + local expanded = mp.command_native({"expand-text", text}) if not expanded then msg.error("Error expanding status-line") return @@ -126,10 +126,8 @@ local function toggle() else enable() end end -if opts.enabled then - enable() -end +if opts.enabled then enable() end -mp.add_key_binding(nil, "status-line-enable" , enable) +mp.add_key_binding(nil, "status-line-enable" , enable ) mp.add_key_binding(nil, "status-line-disable" , disable) -mp.add_key_binding(nil, "status-line-toggle" , toggle) +mp.add_key_binding(nil, "status-line-toggle" , toggle ) diff --git a/scripts/mvi/5_ruler.lua b/scripts/mvi/5_ruler.lua index 33d5fba..4dde318 100644 --- a/scripts/mvi/5_ruler.lua +++ b/scripts/mvi/5_ruler.lua @@ -318,9 +318,9 @@ local function stop() mp.unregister_idle(refresh) mp.unobserve_property(mark_stale) remove_bindings() - state = 0 - first_point = nil - second_point = nil + state = 0 + first_point = nil + second_point = nil draw_ass("") end From d39da2ca7d6dc780811b6135bbafd1454f5d7378 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:29:58 +0700 Subject: [PATCH 05/81] rename to mpvi --- script-opts/{mvi => mpvi}/1_detect_image.conf | 0 script-opts/{mvi => mpvi}/2_image_positioning.conf | 0 script-opts/{mvi => mpvi}/3_status_line.conf | 0 script-opts/{mvi => mpvi}/4_minimap.conf | 0 script-opts/{mvi => mpvi}/5_ruler.conf | 0 scripts/{mvi => mpvi}/1_detect_image.lua | 0 scripts/{mvi => mpvi}/2_image_positioning.lua | 0 scripts/{mvi => mpvi}/3_status_line.lua | 0 scripts/{mvi => mpvi}/4_minimap.lua | 0 scripts/{mvi => mpvi}/5_ruler.lua | 0 scripts/{mvi => mpvi}/6_freeze_window.lua | 0 scripts/{mvi => mpvi}/lib/std.lua | 0 scripts/{mvi => mpvi}/main.lua | 0 13 files changed, 0 insertions(+), 0 deletions(-) rename script-opts/{mvi => mpvi}/1_detect_image.conf (100%) rename script-opts/{mvi => mpvi}/2_image_positioning.conf (100%) rename script-opts/{mvi => mpvi}/3_status_line.conf (100%) rename script-opts/{mvi => mpvi}/4_minimap.conf (100%) rename script-opts/{mvi => mpvi}/5_ruler.conf (100%) rename scripts/{mvi => mpvi}/1_detect_image.lua (100%) rename scripts/{mvi => mpvi}/2_image_positioning.lua (100%) rename scripts/{mvi => mpvi}/3_status_line.lua (100%) rename scripts/{mvi => mpvi}/4_minimap.lua (100%) rename scripts/{mvi => mpvi}/5_ruler.lua (100%) rename scripts/{mvi => mpvi}/6_freeze_window.lua (100%) rename scripts/{mvi => mpvi}/lib/std.lua (100%) rename scripts/{mvi => mpvi}/main.lua (100%) diff --git a/script-opts/mvi/1_detect_image.conf b/script-opts/mpvi/1_detect_image.conf similarity index 100% rename from script-opts/mvi/1_detect_image.conf rename to script-opts/mpvi/1_detect_image.conf diff --git a/script-opts/mvi/2_image_positioning.conf b/script-opts/mpvi/2_image_positioning.conf similarity index 100% rename from script-opts/mvi/2_image_positioning.conf rename to script-opts/mpvi/2_image_positioning.conf diff --git a/script-opts/mvi/3_status_line.conf b/script-opts/mpvi/3_status_line.conf similarity index 100% rename from script-opts/mvi/3_status_line.conf rename to script-opts/mpvi/3_status_line.conf diff --git a/script-opts/mvi/4_minimap.conf b/script-opts/mpvi/4_minimap.conf similarity index 100% rename from script-opts/mvi/4_minimap.conf rename to script-opts/mpvi/4_minimap.conf diff --git a/script-opts/mvi/5_ruler.conf b/script-opts/mpvi/5_ruler.conf similarity index 100% rename from script-opts/mvi/5_ruler.conf rename to script-opts/mpvi/5_ruler.conf diff --git a/scripts/mvi/1_detect_image.lua b/scripts/mpvi/1_detect_image.lua similarity index 100% rename from scripts/mvi/1_detect_image.lua rename to scripts/mpvi/1_detect_image.lua diff --git a/scripts/mvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua similarity index 100% rename from scripts/mvi/2_image_positioning.lua rename to scripts/mpvi/2_image_positioning.lua diff --git a/scripts/mvi/3_status_line.lua b/scripts/mpvi/3_status_line.lua similarity index 100% rename from scripts/mvi/3_status_line.lua rename to scripts/mpvi/3_status_line.lua diff --git a/scripts/mvi/4_minimap.lua b/scripts/mpvi/4_minimap.lua similarity index 100% rename from scripts/mvi/4_minimap.lua rename to scripts/mpvi/4_minimap.lua diff --git a/scripts/mvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua similarity index 100% rename from scripts/mvi/5_ruler.lua rename to scripts/mpvi/5_ruler.lua diff --git a/scripts/mvi/6_freeze_window.lua b/scripts/mpvi/6_freeze_window.lua similarity index 100% rename from scripts/mvi/6_freeze_window.lua rename to scripts/mpvi/6_freeze_window.lua diff --git a/scripts/mvi/lib/std.lua b/scripts/mpvi/lib/std.lua similarity index 100% rename from scripts/mvi/lib/std.lua rename to scripts/mpvi/lib/std.lua diff --git a/scripts/mvi/main.lua b/scripts/mpvi/main.lua similarity index 100% rename from scripts/mvi/main.lua rename to scripts/mpvi/main.lua From 5e6f94602b130edd7ee000236bc2803b73f59f45 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:30:47 +0700 Subject: [PATCH 06/81] rename detect image to have the last index --- script-opts/mpvi/{1_detect_image.conf => 9_detect_image.conf} | 0 scripts/mpvi/{1_detect_image.lua => 9_detect_image.lua} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename script-opts/mpvi/{1_detect_image.conf => 9_detect_image.conf} (100%) rename scripts/mpvi/{1_detect_image.lua => 9_detect_image.lua} (100%) diff --git a/script-opts/mpvi/1_detect_image.conf b/script-opts/mpvi/9_detect_image.conf similarity index 100% rename from script-opts/mpvi/1_detect_image.conf rename to script-opts/mpvi/9_detect_image.conf diff --git a/scripts/mpvi/1_detect_image.lua b/scripts/mpvi/9_detect_image.lua similarity index 100% rename from scripts/mpvi/1_detect_image.lua rename to scripts/mpvi/9_detect_image.lua From 3c63d6828fb3f96717f53b972ca0dd9ae83f3ed8 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:32:22 +0700 Subject: [PATCH 07/81] std: add OSD dimension checker to std Valid OSD means not only that 'OSD-dimensions' property returns a table, but also that the values of this table are non-0 --- scripts/mpvi/lib/std.lua | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index b00a6e9..954c07e 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -41,6 +41,17 @@ function std.basename(path) -- get filename from path return path:gsub("(.*[/\\])(.*)" ,"%2") -- (path/)(file) → (file) end +-- MPV-specific functions +function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height positive + local dim = mp.get_property_native("osd-dimensions") + if not dim then return nil, nil, nil end + local ww, wh = dim.w, dim.h + if not (ww > 0 and + wh > 0 ) then return nil, nil, nil end + return dim, ww, wh +end + + return { std = std, } From ebb99db5b8249953e63723c1725b9ea8ada1c728 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:37:08 +0700 Subject: [PATCH 08/81] replace OSD dimension checker with a more robust function from std --- scripts/mpvi/2_image_positioning.lua | 80 ++++++++++++---------------- scripts/mpvi/5_ruler.lua | 17 +++--- 2 files changed, 39 insertions(+), 58 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 08d1271..22ae036 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -42,17 +42,16 @@ local cleanup = nil -- function set up by drag-to-pan/pan-follows cursor and mus local function drag_to_pan_handler(table) if cleanup then cleanup(); cleanup = nil end if table["event"] == "down" then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim, ww, wh = std.getDimOSD(); if not dim then return end 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 video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } local margin = opts.drag_to_pan_margin local move_up = true local move_lateral = true + local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} if not opts.drag_to_pan_move_if_full_view then if dim.ml >= 0 and dim.mr >= 0 then move_lateral = false @@ -71,24 +70,24 @@ local function drag_to_pan_handler(table) pX = video_pan_origin[1] + (mX - mouse_pos_origin[1]) / video_size[1] if 2 * margin > dim.ml + dim.mr then pX = clamp(pX, - (-margin + dim.w / 2) / video_size[1] - 0.5, - ( margin - dim.w / 2) / video_size[1] + 0.5) + (-margin + ww / 2) / video_size[1] - 0.5, + ( margin - ww / 2) / video_size[1] + 0.5) else pX = clamp(pX, - ( margin - dim.w / 2) / video_size[1] + 0.5, - (-margin + dim.w / 2) / video_size[1] - 0.5) + ( margin - ww / 2) / video_size[1] + 0.5, + (-margin + ww / 2) / video_size[1] - 0.5) end end if move_up then pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_size[2] if 2 * margin > dim.mt + dim.mb then pY = clamp(pY, - (-margin + dim.h / 2) / video_size[2] - 0.5, - ( margin - dim.h / 2) / video_size[2] + 0.5) + (-margin + wh / 2) / video_size[2] - 0.5, + ( margin - wh / 2) / video_size[2] + 0.5) else pY = clamp(pY, - ( margin - dim.h / 2) / video_size[2] + 0.5, - (-margin + dim.h / 2) / video_size[2] - 0.5) + ( margin - wh / 2) / video_size[2] + 0.5, + (-margin + wh / 2) / video_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)) @@ -110,15 +109,14 @@ local function pan_follows_cursor_handler(table) cleanup = nil end if table["event"] == "down" then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local dim, ww, wh = std.getDimOSD(); if not dim then return end + local video_size = {ww - dim.ml - dim.mr, w.h - dim.mt - dim.mb} 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 / dim.w + 1, -1)) - local y = math.min(1, math.max(- 2 * mY / dim.h + 1, -1)) + local x = math.min(1, math.max(- 2 * mX / ww + 1, -1)) + local y = math.min(1, math.max(- 2 * mY / wh + 1, -1)) local command = "" local margin = opts.pan_follows_cursor_margin if dim.ml + dim.mr < 0 then @@ -159,13 +157,13 @@ local function cursor_centric_zoom_handler(amt) local diff_height = (2 ^ zoom_inc - 1) * video_size[2] if not opts.cursor_centric_zoom_dezoom_if_full_view and zoom_inc < 0 and - video_size[1] + diff_width + 2 * margin <= dim.w and - video_size[2] + diff_height + 2 * margin <= dim.h + video_size[1] + diff_width + 2 * margin <= ww and + video_size[2] + diff_height + 2 * margin <= wh then -- the zoom decrement is too much, reduce it such that the full image is visible, no more, no less -- in addition, this should take care of trying too zoom out while everything is already visible - local new_zoom_inc_x = math.log((dim.w - 2 * margin) / video_size[1]) / math.log(2) - local new_zoom_inc_y = math.log((dim.h - 2 * margin) / video_size[2]) / math.log(2) + local new_zoom_inc_x = math.log((ww - 2 * margin) / video_size[1]) / math.log(2) + local new_zoom_inc_y = math.log((wh - 2 * margin) / video_size[2]) / math.log(2) local new_zoom_inc = math.min(0, math.min(new_zoom_inc_x, new_zoom_inc_y)) zoom_inc = new_zoom_inc diff_width = (2 ^ zoom_inc - 1) * video_size[1] @@ -182,14 +180,16 @@ local function cursor_centric_zoom_handler(amt) -- if image can be fully visible (in either direction), set pan to 0 -- if border would show on either side, then prefer adjusting the pan even if not cursor-centric local auto_c = opts.cursor_centric_zoom_auto_center - if auto_c and video_size[1] + diff_width + 2 * margin <= dim.w then + if auto_c and video_size[1] + diff_width + 2 * margin <= ww then new_pan_x = 0 else local pan_x = mp.get_property("video-pan-x") local rx = (dim.ml + video_size[1] / 2 - mouse_pos_origin[1]) / (video_size[1] / 2) new_pan_x = (pan_x * video_size[1] + rx * diff_width / 2) / new_width if auto_c then - new_pan_x = clamp(new_pan_x, (dim.w - 2 * margin) / (2 * new_width) - 0.5, - (dim.w - 2 * margin) / (2 * new_width) + 0.5) + new_pan_x = clamp(new_pan_x, + (ww - 2 * margin) / (2 * new_width) - 0.5, + -(ww - 2 * margin) / (2 * new_width) + 0.5) end end @@ -200,7 +200,9 @@ local function cursor_centric_zoom_handler(amt) local ry = (dim.mt + video_size[2] / 2 - mouse_pos_origin[2]) / (video_size[2] / 2) new_pan_y = (pan_y * video_size[2] + ry * diff_height / 2) / new_height if auto_c then - new_pan_y = clamp(new_pan_y, (dim.h - 2 * margin) / (2 * new_height) - 0.5, - (dim.h - 2 * margin) / (2 * new_height) + 0.5) + new_pan_y = clamp(new_pan_y, + (wh - 2 * margin) / (2 * new_height) - 0.5, + -(wh - 2 * margin) / (2 * new_height) + 0.5) end end @@ -209,22 +211,8 @@ local function cursor_centric_zoom_handler(amt) end local function align_border(x, y) - -- local dim_s = mp.get_property("osd-dimensions"); print("dim_s"..tostring(dim_s)) - - local dim = mp.get_property_native("osd-dimensions") - local dimw = dim.w - -- if not dim.w then print("returning on zero dim.w=¦"..tostring(dim.w)); return; end - -- if not dim then return end - print("dimw=¦"..tostring(dimw).."¦") - if dimw==0 then - print("returning on zero dimw=¦"..dimw.."¦") - return - else - print("not returning no nonzero dimw=¦"..dimw.."¦") - end - print("dim=¦"..tostring(dim).."¦") - print("dim.w=¦"..tostring(dim.w).."¦") - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local dim, ww, wh = std.getDimOSD(); if not dim then return end + local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb } local x, y = tonumber(x), tonumber(y) local command = "" print("w=¦"..tostring(dim.w) .."¦".."ml=¦"..tostring(dim.ml) .."¦".."mr=¦"..tostring(dim.mr) .."¦".."h=¦"..tostring(dim.h) .."¦".."mt=¦"..tostring(dim.mt) .."¦".."mb=¦"..tostring(dim.mb) .."¦") @@ -246,14 +234,13 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) local prop = "video-pan-" .. axis local old_pan = mp.get_property_number(prop) if image_constrained == "yes" then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim, ww, wh = std.getDimOSD(); if not dim then return end local margin = - (axis == "x" and amount > 0) and dim.ml + (axis == "x" and amount > 0) and dim.ml or (axis == "x" and amount < 0) and dim.mr - or (amount > 0) and dim.mt - or (amount < 0) and dim.mb - local vid_size = (axis == "x") and (dim.w - dim.ml - dim.mr) or (dim.h - dim.mt - dim.mb) + or ( amount > 0) and dim.mt + or ( amount < 0) and dim.mb + local vid_size = (axis == "x") and (ww - dim.ml - dim.mr) or (wh - dim.mt - dim.mb) local pixels_moved = math.abs(amount) * vid_size -- the margin is already visible, no point going further if margin >= 0 then @@ -272,8 +259,7 @@ local function rotate_video(amt) end local function reset_pan_if_visible() - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim = std.getDimOSD(); if not dim then return end local command = "" if (dim.ml + dim.mr >= 0) then command = command .. "no-osd set video-pan-x 0" .. ";" diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua index 4dde318..6bfd0f9 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/5_ruler.lua @@ -76,13 +76,10 @@ local function refresh() if not video_dimensions_stale then return end video_dimensions_stale = false - local dim = mp.get_property_native("osd-dimensions") - local out_params = mp.get_property_native("video-out-params") - if not dim or not out_params then - draw_ass("") - return - end - local vid_width = out_params.dw + local dim = std.getDimOSD() + local out_params = mp.get_property_native("video-out-params") + if not dim or not out_params then draw_ass(""); return end + local vid_width = out_params.dw local vid_height = out_params.dh function video_space_normalized_to_video(point) @@ -296,13 +293,11 @@ local function next_step() next_step() end elseif state == 1 then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim = std.getDimOSD(); if not dim then return end state = 2 first_point = cursor_video_space_normalized(dim) elseif state == 2 then - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim = std.getDimOSD(); if not dim then return end state = 3 second_point = cursor_video_space_normalized(dim) if opts.clear_on_second_point_set then From f22279ffb64aca24767b18d6f8cfd4e32aae33ef Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:38:09 +0700 Subject: [PATCH 09/81] reformat script path var comments --- scripts/mpvi/2_image_positioning.lua | 8 ++++---- scripts/mpvi/3_status_line.lua | 8 ++++---- scripts/mpvi/4_minimap.lua | 8 ++++---- scripts/mpvi/5_ruler.lua | 8 ++++---- scripts/mpvi/9_detect_image.lua | 8 ++++---- scripts/mpvi/main.lua | 2 +- 6 files changed, 21 insertions(+), 21 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 22ae036..d5bd35d 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -9,12 +9,12 @@ local std = require "lib/std".std -local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi -local script_dir_base = std.basename(script_dir) -- mvi -local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi +local script_dir_base = std.basename(script_dir) -- mpvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mpvi/.lua local script_file_name = std.filename(script_path) -- .lua local script_stem = std.delua(script_file_name) -- -local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { drag_to_pan_margin = 50, diff --git a/scripts/mpvi/3_status_line.lua b/scripts/mpvi/3_status_line.lua index 14c517e..1674ebb 100644 --- a/scripts/mpvi/3_status_line.lua +++ b/scripts/mpvi/3_status_line.lua @@ -3,12 +3,12 @@ local std = require "lib/std".std -local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi -local script_dir_base = std.basename(script_dir) -- mvi -local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi +local script_dir_base = std.basename(script_dir) -- mpvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mpvi/.lua local script_file_name = std.filename(script_path) -- .lua local script_stem = std.delua(script_file_name) -- -local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { enabled = true, diff --git a/scripts/mpvi/4_minimap.lua b/scripts/mpvi/4_minimap.lua index d6d4d97..44b9620 100644 --- a/scripts/mpvi/4_minimap.lua +++ b/scripts/mpvi/4_minimap.lua @@ -3,12 +3,12 @@ local std = require "lib/std".std -local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi -local script_dir_base = std.basename(script_dir) -- mvi -local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi +local script_dir_base = std.basename(script_dir) -- mpvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mpvi/.lua local script_file_name = std.filename(script_path) -- .lua local script_stem = std.delua(script_file_name) -- -local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { enabled = true, diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua index 6bfd0f9..4d3f639 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/5_ruler.lua @@ -3,12 +3,12 @@ local std = require "lib/std".std -local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi -local script_dir_base = std.basename(script_dir) -- mvi -local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi +local script_dir_base = std.basename(script_dir) -- mpvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mpvi/.lua local script_file_name = std.filename(script_path) -- .lua local script_stem = std.delua(script_file_name) -- -local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { show_distance = true, diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/9_detect_image.lua index 016dc7a..32d2815 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/9_detect_image.lua @@ -2,12 +2,12 @@ local std = require "lib/std".std -local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi -local script_dir_base = std.basename(script_dir) -- mvi -local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mvi/.lua +local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi +local script_dir_base = std.basename(script_dir) -- mpvi +local script_path = std.getScriptFullPath() -- ~/.config/mpv/scripts/mpvi/.lua local script_file_name = std.filename(script_path) -- .lua local script_stem = std.delua(script_file_name) -- -local opt_path_rel = script_dir_base ..'/'.. script_stem -- mvi/ +local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { on_load_image_first = "", diff --git a/scripts/mpvi/main.lua b/scripts/mpvi/main.lua index 93e9ed4..3142df5 100644 --- a/scripts/mpvi/main.lua +++ b/scripts/mpvi/main.lua @@ -1,6 +1,6 @@ local std = require "lib/std".std -local script_directory = mp.get_script_directory() -- ~/.config/mpv/scripts/mvi +local script_directory = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi function load(relative_path) if relative_path:endswith('.lua') then relative_path = relative_path else relative_path = relative_path .. ".lua" end From e802c03615b645f97919f273fe0b596d9ad83151 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:39:09 +0700 Subject: [PATCH 10/81] image pos: remove test function --- scripts/mpvi/2_image_positioning.lua | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index d5bd35d..3fa9e2c 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -272,19 +272,6 @@ local function reset_pan_if_visible() end end -function ttt(x, y) - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } - local x, y = tonumber(x), tonumber(y) - local command = "" - print("w=¦"..tostring(dim.w) .."¦".."ml=¦"..tostring(dim.ml) .."¦".."mr=¦"..tostring(dim.mr) .."¦".."h=¦"..tostring(dim.h) .."¦".."mt=¦"..tostring(dim.mt) .."¦".."mb=¦"..tostring(dim.mb) .."¦") - print("x=¦"..tostring(x) .. "¦y=¦"..tostring(y).."¦".."video_size=¦"..tostring(video_size[1]).."/"..tostring(video_size[2]).."¦") - if x then command = command .."no-osd set video-pan-x ".. clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]), -3, 3) .. ";" end - if y then command = command .."no-osd set video-pan-y ".. clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]), -3, 3) .. ";" end - print("cmd after y="..tostring(command)) -end -mp.add_key_binding(nil, "ttt" , ttt) 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}) From 7933d6d13faa8ec131245cf05faa86fda4470685 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:40:15 +0700 Subject: [PATCH 11/81] image pos: reformat --- scripts/mpvi/2_image_positioning.lua | 57 ++++++++++------------------ 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 3fa9e2c..e6b6cb6 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -46,19 +46,17 @@ local function drag_to_pan_handler(table) 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 + video_pan_origin[1] = mp.get_property_number("video-pan-x") + video_pan_origin[2] = mp.get_property_number("video-pan-y") local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} + 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 dim.ml >= 0 and dim.mr >= 0 then - move_lateral = false - end - if dim.mt >= 0 and dim.mb >= 0 then - move_up = false - end + if dim.ml >= 0 and + dim.mr >= 0 then move_lateral = false end + if dim.mt >= 0 and + dim.mb >= 0 then move_up = false end end if not move_up and not move_lateral then return end local idle = function() @@ -169,7 +167,7 @@ local function cursor_centric_zoom_handler(amt) diff_width = (2 ^ zoom_inc - 1) * video_size[1] diff_height = (2 ^ zoom_inc - 1) * video_size[2] end - local new_width = video_size[1] + diff_width + local new_width = video_size[1] + diff_width local new_height = video_size[2] + diff_height local mouse_pos_origin = {} @@ -214,15 +212,10 @@ local function align_border(x, y) local dim, ww, wh = std.getDimOSD(); if not dim then return end local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb } local x, y = tonumber(x), tonumber(y) - local command = "" - print("w=¦"..tostring(dim.w) .."¦".."ml=¦"..tostring(dim.ml) .."¦".."mr=¦"..tostring(dim.mr) .."¦".."h=¦"..tostring(dim.h) .."¦".."mt=¦"..tostring(dim.mt) .."¦".."mb=¦"..tostring(dim.mb) .."¦") - print("x=¦"..tostring(x) .. "¦y=¦"..tostring(y).."¦".."video_size=¦"..tostring(video_size[1]).."/"..tostring(video_size[2]).."¦") - if x then command = command .."no-osd set video-pan-x ".. clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]), -3, 3) .. ";" end - if y then command = command .."no-osd set video-pan-y ".. clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]), -3, 3) .. ";" end - print("cmd after y="..tostring(command)) - if command ~= "" then - mp.command(command) - end + local cmd = "" + if x then cmd = cmd .."no-osd set video-pan-x "..clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]),-3,3)..";" end + if y then cmd = cmd .."no-osd set video-pan-y "..clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]),-3,3)..";" end + if cmd ~= "" then mp.command(cmd) end end local function pan_image(axis, amount, zoom_invariant, image_constrained) @@ -231,8 +224,8 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) 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) + local prop = "video-pan-" .. axis + local old_pan = mp.get_property_number(prop) if image_constrained == "yes" then local dim, ww, wh = std.getDimOSD(); if not dim then return end local margin = @@ -242,10 +235,8 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) or ( amount < 0) and dim.mb local vid_size = (axis == "x") and (ww - dim.ml - dim.mr) or (wh - dim.mt - dim.mb) local pixels_moved = math.abs(amount) * vid_size - -- the margin is already visible, no point going further - if margin >= 0 then - return - elseif margin + pixels_moved > 0 then + if margin >= 0 then return -- the margin is already visible, no point going further + elseif margin + pixels_moved > 0 then amount = -(math.abs(amount) / amount) * margin / vid_size end end @@ -261,15 +252,9 @@ end local function reset_pan_if_visible() local dim = std.getDimOSD(); if not dim then return end local command = "" - if (dim.ml + dim.mr >= 0) then - command = command .. "no-osd set video-pan-x 0" .. ";" - end - if (dim.mt + dim.mb >= 0) then - command = command .. "no-osd set video-pan-y 0" .. ";" - end - if command ~= "" then - mp.command(command) - end + if (dim.ml + dim.mr >= 0) then command = command .. "no-osd set video-pan-x 0" .. ";" end + if (dim.mt + dim.mb >= 0) then command = command .. "no-osd set video-pan-y 0" .. ";" end + if command ~= "" then mp.command(command) end end From 0738ed2bf530e0097b55eacca366820b204c2eb6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:40:36 +0700 Subject: [PATCH 12/81] replace OSD dimension checker with a more robust function from std --- scripts/mpvi/2_image_positioning.lua | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index e6b6cb6..87e3c2f 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -143,12 +143,11 @@ end local function cursor_centric_zoom_handler(amt) local zoom_inc = tonumber(amt) if not zoom_inc or zoom_inc == 0 then return end - local dim = mp.get_property_native("osd-dimensions") - if not dim then return end + local dim, ww, wh = std.getDimOSD(); if not dim then return end local margin = opts.cursor_centric_zoom_margin - local video_size = { dim.w - dim.ml - dim.mr, dim.h - dim.mt - dim.mb } + local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} -- the size in pixels of the (in|de)crement local diff_width = (2 ^ zoom_inc - 1) * video_size[1] From e47936ea060e787202580262c40c8055bf9f01de Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 19:41:42 +0700 Subject: [PATCH 13/81] ruler: reformat --- scripts/mpvi/5_ruler.lua | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua index 4d3f639..f2e23ae 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/5_ruler.lua @@ -289,9 +289,7 @@ local function next_step() mp.observe_property("osd-dimensions", nil, mark_stale) mark_stale() add_bindings() - if opts.set_first_point_on_begin then - next_step() - end + if opts.set_first_point_on_begin then next_step() end elseif state == 1 then local dim = std.getDimOSD(); if not dim then return end state = 2 @@ -300,12 +298,8 @@ local function next_step() local dim = std.getDimOSD(); if not dim then return end state = 3 second_point = cursor_video_space_normalized(dim) - if opts.clear_on_second_point_set then - next_step() - end - else - stop() - end + if opts.clear_on_second_point_set then next_step() end + else stop() end end local function stop() From 8823a1fa66650ee53cba60a0882af852eb633af9 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:06:51 +0700 Subject: [PATCH 14/81] minimap: replace legacy API with OSD overlay Avoids conflicts with other script within the same folder since it provides unique IDs --- scripts/mpvi/4_minimap.lua | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/scripts/mpvi/4_minimap.lua b/scripts/mpvi/4_minimap.lua index 44b9620..0673f50 100644 --- a/scripts/mpvi/4_minimap.lua +++ b/scripts/mpvi/4_minimap.lua @@ -47,33 +47,33 @@ end local active = false local refresh = true -local function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) -end +local ov = mp.create_osd_overlay("ass-events") local function mark_stale() refresh = true +local function hide_ov() + ov.data="" + ov:remove() +end +local function draw_ov(asstxt) + local ww, wh, par = mp.get_osd_size() + if not (ww > 0 and + wh > 0 ) then return end + ov.res_x, ov.res_y = ww, wh + ov.data = asstxt + ov:update() end local function refresh_minimap() if not refresh then return end refresh = false - local dim = mp.get_property_native("osd-dimensions") - if not dim then - draw_ass("") - return - end - local ww, wh = dim.w, dim.h - - if not (ww > 0 and wh > 0) then return end + local dim, ww, wh = std.getDimOSD(); if not dim then hide_ov(); return end if opts.hide_when_full_image_in_view then - if dim.mt >= 0 and dim.mb >= 0 and dim.ml >= 0 and dim.mr >= 0 then - draw_ass("") - return - end - end + if (dim.mt >= 0 and + dim.mb >= 0 and + dim.ml >= 0 and + dim.mr >= 0 ) then hide_ov(); return end end local center = split_comma(opts.center) center[1] = center[1] * 0.01 * ww @@ -157,7 +157,7 @@ local function refresh_minimap() end if opts.view_above_image then image(); view() else view(); image() end - draw_ass(a.text) + draw_ov(a.text) end local function enable() @@ -173,7 +173,7 @@ local function disable() active = false mp.unobserve_property(mark_stale) mp.unregister_idle(refresh_minimap) - draw_ass("") + hide_ov() end local function toggle() From 328a9862b7168bcf26fc87884c85b59ba7125b22 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:07:24 +0700 Subject: [PATCH 15/81] minimap: reformat --- scripts/mpvi/4_minimap.lua | 94 ++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 55 deletions(-) diff --git a/scripts/mpvi/4_minimap.lua b/scripts/mpvi/4_minimap.lua index 0673f50..e76b20a 100644 --- a/scripts/mpvi/4_minimap.lua +++ b/scripts/mpvi/4_minimap.lua @@ -42,15 +42,12 @@ local function split_comma(input) end return ret end --- msg.info("center = " .. tostring(opts.center)) -local active = false -local refresh = true +local active = false +local refresh = true local ov = mp.create_osd_overlay("ass-events") -local function mark_stale() - refresh = true local function hide_ov() ov.data="" ov:remove() @@ -64,6 +61,8 @@ local function draw_ov(asstxt) ov:update() end +local function mark_stale() refresh = true end + local function refresh_minimap() if not refresh then return end refresh = false @@ -86,18 +85,19 @@ local function refresh_minimap() local draw = function(x, y, w, h, opacity, color) a:new_event() a:pos(center[1], center[2]) - a:append("{\\bord0}") - a:append("{\\shad0}") - a:append("{\\c&" .. color .. "&}") - a:append("{\\2a&HFF}") - a:append("{\\3a&HFF}") - a:append("{\\4a&HFF}") - a:append("{\\1a&H" .. opacity .. "}") + a:append("{\\bord0}") -- border width + a:append("{\\shad0}") -- shadow width + a:append("{\\c&"..color.."&}") -- + a:append("{\\2a&HFF}") -- \2a sets the secondary fill alpha (only used for pre-highlight in standard karaoke) + a:append("{\\3a&HFF}") -- \3a sets the border alpha + a:append("{\\4a&HFF}") -- \4a sets the shadow alpha + a:append("{\\1a&H"..opacity.."}") -- \1a sets the primary fill alpha w = w * 0.5 h = h * 0.5 a:draw_start() local rounded = {true,true,true,true} -- tl, tr, br, bl - local x0,y0,x1,y1 = x-w, y-h, x+w, y+h + local x0,y0 = x-w, y-h + local x1,y1 = x+w, y+h if x0 < -cutoff[1] then x0 = -cutoff[1] rounded[4] = false @@ -121,58 +121,42 @@ local function refresh_minimap() local r = 3 local c = 0.551915024494 * r - if rounded[0] then - a:move_to(x0 + r, y0 ) - else a:move_to(x0 , y0 ) end - if rounded[1] then - a:line_to(x1 - r, y0 ); a:bezier_curve(x1 - r + c, y0 , x1 , y0 + r - c, x1 , y0 + r) - else a:line_to(x1 , y0 ) end - if rounded[2] then - a:line_to(x1 , y1 - r); a:bezier_curve(x1 , y1 - r + c, x1 - r + c, y1 , x1 - r, y1 ) - else a:line_to(x1 , y1 ) end - if rounded[3] then - a:line_to(x0 + r, y1 ); a:bezier_curve(x0 + r - c, y1 , x0 , y1 - r + c, x0 , y1 - r) - else a:line_to(x0 , y1 ) end - if rounded[4] then - a:line_to(x0 , y0 + r); a:bezier_curve(x0 , y0 + r - c, x0 + r - c, y0 , x0 + r, y0 ) - else a:line_to(x0 , y0 ) end + if rounded[0] then a:move_to(x0+r,y0 ) + else a:move_to(x0 ,y0 ) end + if rounded[1] then a:line_to(x1-r,y0 ); a:bezier_curve(x1-r+c,y0 ,x1 ,y0+r-c,x1 ,y0+r) + else a:line_to(x1 ,y0 ) end + if rounded[2] then a:line_to(x1 ,y1-r); a:bezier_curve(x1 ,y1-r+c,x1-r+c,y1 ,x1-r,y1 ) + else a:line_to(x1 ,y1 ) end + if rounded[3] then a:line_to(x0+r,y1 ); a:bezier_curve(x0+r-c,y1 ,x0 ,y1-r+c,x0 ,y1-r) + else a:line_to(x0 ,y1 ) end + if rounded[4] then a:line_to(x0 ,y0+r); a:bezier_curve(x0 ,y0+r-c,x0+r-c,y0 ,x0+r,y0 ) + else a:line_to(x0 ,y0 ) end a:draw_stop() end - local image = function() - draw( - ( dim.ml/2 - dim.mr/2) / opts.scale, - ( dim.mt/2 - dim.mb/2) / opts.scale, - (ww - dim.ml - dim.mr ) / opts.scale, - (wh - dim.mt - dim.mb ) / opts.scale, - opts.image_opacity, - opts.image_color) - end - local view = function() - draw(0, - 0, - ww / opts.scale, - wh / opts.scale, - opts.view_opacity, - opts.view_color) - end - if opts.view_above_image then image(); view() - else view(); image() end + local draw_image = function() draw( + ( dim.ml/2 - dim.mr/2) / opts.scale, ( dim.mt/2 - dim.mb/2) / opts.scale, + (ww - dim.ml - dim.mr ) / opts.scale, (wh - dim.mt - dim.mb ) / opts.scale, + opts.image_opacity, opts.image_color) end + local draw_view = function() draw( + 0 , 0, + ww / opts.scale, wh / opts.scale, + opts.view_opacity , opts.view_color ) end + if opts.view_above_image then draw_image(); draw_view () + else draw_view (); draw_image() end draw_ov(a.text) end local function enable() - if active then return end - active = true - mp.observe_property("osd-dimensions", nil, mark_stale) - mp.register_idle(refresh_minimap) + if active then return else active = true end + mp.observe_property ("osd-dimensions" , nil, mark_stale); mp.register_idle (refresh_minimap) + -- call this whenever OSD dimensions change ↑ helps to catch OSD init on launch + -- delay refresh until all notifications have been received ↑ mark_stale() end local function disable() - if not active then return end - active = false - mp.unobserve_property(mark_stale) - mp.unregister_idle(refresh_minimap) + if not active then return else active = false end + mp.unobserve_property( mark_stale); mp.unregister_idle(refresh_minimap) hide_ov() end From 4c0d3315d02eea278487daac8cca8d33f0e23b10 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:24:32 +0700 Subject: [PATCH 16/81] minimap: reformat --- scripts/mpvi/4_minimap.lua | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/scripts/mpvi/4_minimap.lua b/scripts/mpvi/4_minimap.lua index e76b20a..4f27469 100644 --- a/scripts/mpvi/4_minimap.lua +++ b/scripts/mpvi/4_minimap.lua @@ -63,7 +63,7 @@ end local function mark_stale() refresh = true end -local function refresh_minimap() +local function refresh_ui() if not refresh then return end refresh = false @@ -148,21 +148,19 @@ end local function enable() if active then return else active = true end - mp.observe_property ("osd-dimensions" , nil, mark_stale); mp.register_idle (refresh_minimap) + mp.observe_property ("osd-dimensions" , nil, mark_stale); mp.register_idle (refresh_ui) -- call this whenever OSD dimensions change ↑ helps to catch OSD init on launch -- delay refresh until all notifications have been received ↑ mark_stale() end - local function disable() if not active then return else active = false end - mp.unobserve_property( mark_stale); mp.unregister_idle(refresh_minimap) + mp.unobserve_property( mark_stale); mp.unregister_idle(refresh_ui) hide_ov() end - local function toggle() if active then disable() - else enable(); end + else enable() end end if opts.enabled then enable() end From 7bee83abde7d495c2ebdfd844bf7cd160eb56c8f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:31:09 +0700 Subject: [PATCH 17/81] status_line: replace legacy API with OSD overlay Avoids conflicts with other script within the same folder since it provides unique IDs --- scripts/mpvi/3_status_line.lua | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/scripts/mpvi/3_status_line.lua b/scripts/mpvi/3_status_line.lua index 1674ebb..ebead27 100644 --- a/scripts/mpvi/3_status_line.lua +++ b/scripts/mpvi/3_status_line.lua @@ -46,9 +46,19 @@ end) local stale = true local active = false -local function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) +local ov = mp.create_osd_overlay("ass-events") + +local function hide_ov() + ov.data="" + ov:remove() +end +local function draw_ov(asstxt) + local ww, wh, par = mp.get_osd_size() + if not (ww > 0 and + wh > 0 ) then return end + ov.res_x, ov.res_y = ww, wh + ov.data = asstxt + ov:update() end local function refresh() @@ -75,7 +85,7 @@ local function refresh() draw_text(opts.text_top_right , 9, w-m, m) draw_text(opts.text_bottom_left , 1, m, h-m) draw_text(opts.text_bottom_right , 3, w-m, h-m) - draw_ass(a.text) + draw_ov(a.text) end local function mark_stale() @@ -118,7 +128,7 @@ local function disable() active = false observe_properties() mp.unregister_idle(refresh) - draw_ass("") + hide_ov() end local function toggle() From 32ab7c6c981fd4ca37e05eb50be99efaa3181422 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:31:26 +0700 Subject: [PATCH 18/81] status_line: reformat --- scripts/mpvi/3_status_line.lua | 32 +++++++++----------------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/scripts/mpvi/3_status_line.lua b/scripts/mpvi/3_status_line.lua index ebead27..4170fb0 100644 --- a/scripts/mpvi/3_status_line.lua +++ b/scripts/mpvi/3_status_line.lua @@ -25,13 +25,9 @@ local assdraw = require 'mp.assdraw' local options = require 'mp.options' options.read_options(opts, opt_path_rel, function(c) - if c["enabled"] then - if opts.enabled then enable() - else disable() end - end - if c["size"] or c["margin"] then - mark_stale() - end + if c["enabled"] then if opts.enabled then enable() + else disable() end end + if c["size"] or c["margin"] then mark_stale() end if c["text_top_left" ] or c["text_top_right" ] or c["text_bottom_left" ] or @@ -41,7 +37,6 @@ options.read_options(opts, opt_path_rel, function(c) mark_stale() end end) --- msg.info("text_bottom_left = " .. tostring(opts.text_bottom_left)) local stale = true local active = false @@ -61,7 +56,7 @@ local function draw_ov(asstxt) ov:update() end -local function refresh() +local function refresh_ui() if not stale then return end stale = false local a = assdraw:ass_new() @@ -88,9 +83,7 @@ local function refresh() draw_ov(a.text) end -local function mark_stale() - stale = true -end +local function mark_stale() stale = true end local function observe_properties() mp.unobserve_property(mark_stale) @@ -115,22 +108,15 @@ local function observe_properties() end local function enable() - if active then return end - active = true - observe_properties() - mp.register_idle(refresh) + if active then return else active = true end + observe_properties(); mp.register_idle( refresh_ui) mark_stale() end - - local function disable() - if not active then return end - active = false - observe_properties() - mp.unregister_idle(refresh) + if not active then return else active = false end + observe_properties(); mp.unregister_idle(refresh_ui) hide_ov() end - local function toggle() if active then disable() else enable() end From 671cb13d110c84268f30ec36f92bd84e7d7330df Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:44:49 +0700 Subject: [PATCH 19/81] ruler: replace legacy API with OSD overlay Avoids conflicts with other script within the same folder since it provides unique IDs --- scripts/mpvi/5_ruler.lua | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua index f2e23ae..8335a1f 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/5_ruler.lua @@ -58,9 +58,19 @@ options.read_options(opts, opt_path_rel, function() end) -- msg.info("confirm_bindings = " .. tostring(opts.confirm_bindings)) -local function draw_ass(ass) - local ww, wh = mp.get_osd_size() - mp.set_osd_ass(ww, wh, ass) +local ov = mp.create_osd_overlay("ass-events") + +local function hide_ov() + ov.data="" + ov:remove() +end +local function draw_ov(asstxt) + local ww, wh, par = mp.get_osd_size() + if not (ww > 0 and + wh > 0 ) then return end + ov.res_x, ov.res_y = ww, wh + ov.data = asstxt + ov:update() end @@ -78,7 +88,7 @@ local function refresh() local dim = std.getDimOSD() local out_params = mp.get_property_native("video-out-params") - if not dim or not out_params then draw_ass(""); return end + if not dim or not out_params then hide_ov(); return end local vid_width = out_params.dw local vid_height = out_params.dh @@ -255,7 +265,7 @@ local function refresh() end end - draw_ass(a.text) + draw_ov(a.text) end local function mark_stale() @@ -310,7 +320,7 @@ local function stop() state = 0 first_point = nil second_point = nil - draw_ass("") + hide_ov() end mp.add_key_binding(nil, "ruler", next_step) From e78dc02ef40d4c128a0ddb7a08268156887456a6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:44:59 +0700 Subject: [PATCH 20/81] ruler: reformat --- scripts/mpvi/5_ruler.lua | 49 ++++++++++++---------------------------- 1 file changed, 15 insertions(+), 34 deletions(-) diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/5_ruler.lua index 8335a1f..6eeffbf 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/5_ruler.lua @@ -56,7 +56,6 @@ options.read_options(opts, opt_path_rel, function() mark_stale() end end) --- msg.info("confirm_bindings = " .. tostring(opts.confirm_bindings)) local ov = mp.create_osd_overlay("ass-events") @@ -228,27 +227,20 @@ local function refresh() local angle = math.atan(math.abs(line_start.image[2] - line_end.image[2]) / math.abs(line_start.image[1] - line_end.image[1])) local fix_angle local an - if line_end.image[2] < line_start.image[2] and line_end.image[1] > line_start.image[1] then - -- upper-right - an = 4 - fix_angle = function(angle) return - angle end + if line_end.image[2] < line_start.image[2] and + line_end.image[1] > line_start.image[1] then + an = 4; fix_angle = function(angle) return - angle end -- upper-right elseif line_end.image[2] < line_start.image[2] then - -- upper-left - an = 6 - fix_angle = function(angle) return math.pi + angle end + an = 6; fix_angle = function(angle) return math.pi + angle end -- upper-left elseif line_end.image[1] < line_start.image[1] then - -- bottom-left - an = 6 - fix_angle = function(angle) return math.pi - angle end + an = 6; fix_angle = function(angle) return math.pi - angle end -- bottom-left else - -- bottom-right - an = 4 - fix_angle = function(angle) return angle end + an = 4; fix_angle = function(angle) return angle end -- bottom-right end -- should implement this https://math.stackexchange.com/questions/873224/calculate-control-points-of-cubic-bezier-curve-approximating-a-part-of-a-circle local cp1 = pos_from_angle(1, fix_angle(angle*1/4)) local cp2 = pos_from_angle(1, fix_angle(angle*3/4)) - local p2 = pos_from_angle(1, fix_angle(angle)) + local p2 = pos_from_angle(1, fix_angle(angle )) a:bezier_curve(cp1[1], cp1[2], cp2[1], cp2[2], p2[1], p2[2]) a:new_event() @@ -256,12 +248,10 @@ local function refresh() local text_pos = pos_from_angle(1.1, fix_angle(angle*2/3)) -- you'd think /2 would make more sense, but *2/3 looks better a:pos(text_pos[1], text_pos[2]) a:an(an) - if opts.show_angles == "both" then - a:append(string.format("%.2f\\N%.1f°", angle, angle / math.pi * 180)) - elseif opts.show_angles == "degrees" then - a:append(string.format("%.1f°", angle / math.pi * 180)) - elseif opts.show_angles == "radians" then - a:append(string.format("%.2f", angle)) + local angle_rad = angle / math.pi * 180 + if opts.show_angles == "both" then a:append(string.format("%.2f\\N%.1f°",angle,angle_rad)) + elseif opts.show_angles == "degrees" then a:append(string.format("%.1f°" ,angle_rad)) + elseif opts.show_angles == "radians" then a:append(string.format("%.2f" ,angle )) end end @@ -274,21 +264,12 @@ end local function add_bindings() mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) - for _, key in ipairs(confirm_bindings) do - mp.add_forced_key_binding(key, "ruler-next-" .. key, next_step) - end - for _, key in ipairs(exit_bindings) do - mp.add_forced_key_binding(key, "ruler-stop-" .. key, stop) - end + for _, key in ipairs(confirm_bindings) do mp.add_forced_key_binding(key,"ruler-next-"..key,next_step) end + for _, key in ipairs(exit_bindings) do mp.add_forced_key_binding(key,"ruler-stop-"..key,stop ) end end - local function remove_bindings() - for _, key in ipairs(confirm_bindings) do - mp.remove_key_binding("ruler-next-" .. key) - end - for _, key in ipairs(exit_bindings) do - mp.remove_key_binding("ruler-stop-" .. key) - end + for _, key in ipairs(confirm_bindings) do mp.remove_key_binding ( "ruler-next-"..key) end + for _, key in ipairs(exit_bindings) do mp.remove_key_binding ( "ruler-stop-"..key) end mp.remove_key_binding("ruler-mouse-move") end From 9335066bed59bfb7824c39a65c1ea5db168f77c6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:49:29 +0700 Subject: [PATCH 21/81] detect image: remove debug prints --- scripts/mpvi/9_detect_image.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/9_detect_image.lua index 32d2815..c95aa6f 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/9_detect_image.lua @@ -18,9 +18,6 @@ local options = require 'mp.options' local msg = require 'mp.msg' options.read_options(opts, opt_path_rel, function() end) --- msg.info("on_load_image_first = " .. tostring(opts.on_load_image_first)) --- msg.info("on_load_image = " .. tostring(opts.on_load_image)) --- msg.info("on_load_non_image = " .. tostring(opts.on_load_non_image)) local function run_maybe(str) if str ~= "" then mp.command(str) end From fa77f3b4f0cf56a417e465d8b29d136a56101c71 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:50:39 +0700 Subject: [PATCH 22/81] load detect_image last it sends messages to other scripts, so better load those earlier --- scripts/mpvi/main.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/mpvi/main.lua b/scripts/mpvi/main.lua index 3142df5..2e649ea 100644 --- a/scripts/mpvi/main.lua +++ b/scripts/mpvi/main.lua @@ -7,10 +7,10 @@ function load(relative_path) dofile(script_directory ..'/'.. relative_path) end --- configs are @ script-opts/mvi/.conf -load('1_detect_image' ) -- detect when 🖼 are loaded, allows running commands from .conf +-- configs are @ script-opts/mpvi/.conf load('2_image_positioning' ) -- add several high-level commands to zoom and pan load('3_status_line' ) -- add a status line that can show different properties in the window corner load('4_minimap' ) -- add a minimap that displays the position of the image relative to the view load('5_ruler' ) -- add a `ruler` command that lets you measure positions, distances and angles in the image load('6_freeze_window' ) -- disabled window auto resizing on file changes to fit its size +load('9_detect_image' ) -- detect when 🖼 are loaded, allows running commands from .conf From 1e3d7bbaab31775bfc782c6ed211e22c7985cc59 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:54:50 +0700 Subject: [PATCH 23/81] image position: fix typo --- scripts/mpvi/2_image_positioning.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 87e3c2f..b4f43c6 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -108,7 +108,7 @@ local function pan_follows_cursor_handler(table) end if table["event"] == "down" then local dim, ww, wh = std.getDimOSD(); if not dim then return end - local video_size = {ww - dim.ml - dim.mr, w.h - dim.mt - dim.mb} + local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} local moved = true local idle = function() if moved then From 4af0d14a6602c75d24fb24d5751410d31c31ad68 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 20 Jan 2023 22:59:42 +0700 Subject: [PATCH 24/81] fix typo in detect image config --- script-opts/mpvi/9_detect_image.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/script-opts/mpvi/9_detect_image.conf b/script-opts/mpvi/9_detect_image.conf index cc73c8f..b9168d6 100644 --- a/script-opts/mpvi/9_detect_image.conf +++ b/script-opts/mpvi/9_detect_image.conf @@ -6,6 +6,6 @@ #on_load_image_first = loaded image, and the previous file was not an image (or there was no previous file) #on_load_image = loaded image (regardless of what the previous file was) #on_load_non_image = loaded non-image, and the previous file was an image -on_load_image_first= apply-profile mvi ; enable-section mvi; script-message status-line-enable -on_load_image= no-osd set video-pan-x 0; script-message align-border "" -1 -on_load_non_image= apply-profile mvi restore; disable-section mvi; no-osd set video-pan-y 0; no-osd set video-pan-x 0; no-osd set video-zoom 0; script-message status-line-disable +on_load_image_first= apply-profile mpvi ; enable-section mpvi; script-message status-line-enable +on_load_image= no-osd set video-pan-x 0; script-message align-border "" -1 +on_load_non_image= apply-profile mpvi restore; disable-section mpvi; no-osd set video-pan-y 0; no-osd set video-pan-x 0; no-osd set video-zoom 0; script-message status-line-disable From edff8d9f457f8285e0726352c1abe13ba1735990 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 21:31:52 +0700 Subject: [PATCH 25/81] image pos: add a reminder that -3/3 limits are from MPV --- scripts/mpvi/2_image_positioning.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index b4f43c6..e4c0416 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -27,6 +27,11 @@ local opts = { local options = require 'mp.options' local msg = require 'mp.msg' local assdraw = require 'mp.assdraw' +local pan_min = -3.0 +local pan_max = 3.0 +-- {"video-pan-x", OPT_FLOAT(pan_x), M_RANGE(-3.0, 3.0)}, +-- {"video-pan-y", OPT_FLOAT(pan_y), M_RANGE(-3.0, 3.0)}, +-- https://github.com/mpv-player/mpv/blob/67dbe2a8f46926951af01a4ac91937f283898bb2/options/options.c#L141 options.read_options(opts, opt_path_rel, function() end) -- msg.info("drag_to_pan_margin = " .. tostring(opts.drag_to_pan_margin)) From c517171b6ef03523622ed7dd9a5f1390b6128995 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 21:32:14 +0700 Subject: [PATCH 26/81] image pos: track OSD settings to ensure alignment is set on launch Still not 100% correct as window scaling may emit two OSD property change events, so we trigger alignment on 2 non-zero OSD calls BUT: we can't differentiate whether this was auto-scaling-call on launch or a valid user call, so user's first manual alignment might get overriden by this fix --- scripts/mpvi/2_image_positioning.lua | 42 ++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index e4c0416..a3739fe 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -212,15 +212,44 @@ local function cursor_centric_zoom_handler(amt) mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) end +local track_count = 0 +local track_count_max = 2 -- track first 2 OSD changes on launch until this is implemented https://github.com/mpv-player/mpv/issues/11191 +local isOSD = false +local align_border_init_x = nil +local align_border_init_y = nil local function align_border(x, y) - local dim, ww, wh = std.getDimOSD(); if not dim then return end - local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb } - local x, y = tonumber(x), tonumber(y) - local cmd = "" - if x then cmd = cmd .."no-osd set video-pan-x "..clamp(-x*(dim.ml + dim.mr) / (2*video_size[1]),-3,3)..";" end - if y then cmd = cmd .."no-osd set video-pan-y "..clamp(-y*(dim.mt + dim.mb) / (2*video_size[2]),-3,3)..";" end + local dim, ww, wh = std.getDimOSD() + if not dim then + if not isOSD then -- OSD is not ready, setup for a wait and return + align_border_init_x = x -- store initial parameters so we can use them when OSD appears + align_border_init_y = y + end + return end + + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins + local vid_w = ww - mw + local vid_h = wh - mh + local x,y = tonumber(x),tonumber(y) + local cmd = "" + if x then x = clamp( x*(mw/2)/vid_w,-3,3); cmd = cmd.."no-osd set video-pan-x "..x..";" end + if y then y = clamp(-y*(mh/2)/vid_h,-3,3); cmd = cmd.."no-osd set video-pan-y "..y..";" end if cmd ~= "" then mp.command(cmd) end end +local function align_border_wait_osd() + local dim = std.getDimOSD(); if not dim then return end + isOSD = true + + if align_border_init_x == nil and align_border_init_y == nil then -- OSD+ align_border− not called + if track_count > track_count_max then -- unregister self if called a few times + mp.unobserve_property( align_border_wait_osd) + else track_count = track_count + 1 end -- or not yet, increase the count + else -- OSD+ align_border+ called + if track_count > track_count_max then -- unregister self if called a few times + mp.unobserve_property( align_border_wait_osd) + else track_count = track_count + 1 -- or not yet, +count and +align_border + align_border(align_border_init_x, align_border_init_y) end end +end local function pan_image(axis, amount, zoom_invariant, image_constrained) amount = tonumber(amount) @@ -261,6 +290,7 @@ local function reset_pan_if_visible() if command ~= "" then mp.command(command) end end +mp.observe_property("osd-dimensions",nil,align_border_wait_osd) -- wait for OSD before aligning border 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}) From baba243746a7066813951b5cfcccba03b6fc07a9 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 21:32:30 +0700 Subject: [PATCH 27/81] image pos: reformat --- scripts/mpvi/2_image_positioning.lua | 68 ++++++++++++++++------------ 1 file changed, 38 insertions(+), 30 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index a3739fe..603409c 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -48,12 +48,14 @@ local function drag_to_pan_handler(table) if cleanup then cleanup(); cleanup = nil end if table["event"] == "down" then local dim, ww, wh = std.getDimOSD(); if not dim then return end + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins 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 video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} + local video_size = {ww - mw, wh - mh} local margin = opts.drag_to_pan_margin local move_up = true local move_lateral = true @@ -71,7 +73,7 @@ local function drag_to_pan_handler(table) local pY = video_pan_origin[2] if move_lateral then pX = video_pan_origin[1] + (mX - mouse_pos_origin[1]) / video_size[1] - if 2 * margin > dim.ml + dim.mr then + if 2 * margin > mw then pX = clamp(pX, (-margin + ww / 2) / video_size[1] - 0.5, ( margin - ww / 2) / video_size[1] + 0.5) @@ -83,7 +85,7 @@ local function drag_to_pan_handler(table) end if move_up then pY = video_pan_origin[2] + (mY - mouse_pos_origin[2]) / video_size[2] - if 2 * margin > dim.mt + dim.mb then + if 2 * margin > mh then pY = clamp(pY, (-margin + wh / 2) / video_size[2] - 0.5, ( margin - wh / 2) / video_size[2] + 0.5) @@ -93,7 +95,7 @@ local function drag_to_pan_handler(table) (-margin + wh / 2) / video_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)) + 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 @@ -113,26 +115,28 @@ local function pan_follows_cursor_handler(table) end if table["event"] == "down" then local dim, ww, wh = std.getDimOSD(); if not dim then return end - local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins + local video_size = {ww - mw, wh - mh} 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 / ww + 1, -1)) - local y = math.min(1, math.max(- 2 * mY / wh + 1, -1)) - local command = "" + local x = math.min(1, math.max(-2 * mX / ww + 1, -1)) + local y = math.min(1, math.max(-2 * mY / wh + 1, -1)) + local cmd = "" local margin = opts.pan_follows_cursor_margin - if dim.ml + dim.mr < 0 then - command = command .. "no-osd set video-pan-x " .. clamp(x * (2 * margin - dim.ml - dim.mr) / (2 * video_size[1]), -3, 3) .. ";" + if mw < 0 then + cmd = cmd.."no-osd set video-pan-x "..clamp(x * (2 * margin - mw) / (2 * video_size[1]),-3,3)..";" elseif mp.get_property_number("video-pan-x") ~= 0 then - command = command .. "no-osd set video-pan-x " .. "0;" + cmd = cmd.."no-osd set video-pan-x " .. "0;" end - if dim.mt + dim.mb < 0 then - command = command .. "no-osd set video-pan-y " .. clamp(y * (2 * margin - dim.mt - dim.mb) / (2 * video_size[2]), -3, 3) .. ";" + if mh < 0 then + cmd = cmd.."no-osd set video-pan-y "..clamp(y * (2 * margin - mh) / (2 * video_size[2]),-3,3)..";" elseif mp.get_property_number("video-pan-y") ~= 0 then - command = command .. "no-osd set video-pan-y " .. "0;" + cmd = cmd.."no-osd set video-pan-y " .. "0;" end - if command ~= "" then mp.command(command) end + if cmd ~= "" then mp.command(cmd) end moved = false end end @@ -149,10 +153,12 @@ local function cursor_centric_zoom_handler(amt) local zoom_inc = tonumber(amt) if not zoom_inc or zoom_inc == 0 then return end local dim, ww, wh = std.getDimOSD(); if not dim then return end + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins local margin = opts.cursor_centric_zoom_margin - local video_size = {ww - dim.ml - dim.mr, wh - dim.mt - dim.mb} + local video_size = {ww - mw, wh - mh} -- the size in pixels of the (in|de)crement local diff_width = (2 ^ zoom_inc - 1) * video_size[1] @@ -209,7 +215,7 @@ local function cursor_centric_zoom_handler(amt) end local zoom_origin = mp.get_property("video-zoom") - mp.command("no-osd set video-zoom " .. zoom_origin + zoom_inc .. "; no-osd set video-pan-x " .. clamp(new_pan_x, -3, 3) .. "; no-osd set video-pan-y " .. clamp(new_pan_y, -3, 3)) + mp.command("no-osd set video-zoom "..zoom_origin + zoom_inc.."; no-osd set video-pan-x "..clamp(new_pan_x,-3,3).."; no-osd set video-pan-y "..clamp(new_pan_y,-3,3)) end local track_count = 0 @@ -261,12 +267,14 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) local old_pan = mp.get_property_number(prop) if image_constrained == "yes" then local dim, ww, wh = std.getDimOSD(); if not dim then return end + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins local margin = (axis == "x" and amount > 0) and dim.ml or (axis == "x" and amount < 0) and dim.mr or ( amount > 0) and dim.mt or ( amount < 0) and dim.mb - local vid_size = (axis == "x") and (ww - dim.ml - dim.mr) or (wh - dim.mt - dim.mb) + local vid_size = (axis == "x") and (ww - mw) or (wh - mh) local pixels_moved = math.abs(amount) * vid_size if margin >= 0 then return -- the margin is already visible, no point going further elseif margin + pixels_moved > 0 then @@ -284,19 +292,19 @@ end local function reset_pan_if_visible() local dim = std.getDimOSD(); if not dim then return end - local command = "" - if (dim.ml + dim.mr >= 0) then command = command .. "no-osd set video-pan-x 0" .. ";" end - if (dim.mt + dim.mb >= 0) then command = command .. "no-osd set video-pan-y 0" .. ";" end - if command ~= "" then mp.command(command) end + local cmd = "" + if (mw >= 0) then cmd = cmd.."no-osd set video-pan-x 0"..";" end + if (mh >= 0) then cmd = cmd.."no-osd set video-pan-y 0"..";" end + if cmd ~= "" then mp.command(cmd) end end mp.observe_property("osd-dimensions",nil,align_border_wait_osd) -- wait for OSD before aligning border -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) +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 ) From c954576e82d0ec5677aca63ff5dbe3682f36ab4d Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 21:33:09 +0700 Subject: [PATCH 28/81] change border alignment to more 'natural' directions -1 left/down +1 right/up --- input.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/input.conf b/input.conf index 0eb9f48..94ed5b7 100644 --- a/input.conf +++ b/input.conf @@ -61,10 +61,10 @@ alt+left repeatable script-message pan-image x +0.01 yes yes # align-border takes the following arguments: # align-border ALIGN_X ALIGN_Y # any value for ALIGN_* is accepted, -1 and 1 map to the border of the window -ctrl+shift+right script-message align-border -1 "" -ctrl+shift+left script-message align-border 1 "" +ctrl+shift+right script-message align-border 1 "" +ctrl+shift+left script-message align-border -1 "" +ctrl+shift+up script-message align-border "" 1 ctrl+shift+down script-message align-border "" -1 -ctrl+shift+up script-message align-border "" 1 # reset the image ctrl+0 no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0 From fd6364cccd6d7071563af42e8bcb221319fbf0a2 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 21:58:02 +0700 Subject: [PATCH 29/81] image pos: fix missing mw/mh --- scripts/mpvi/2_image_positioning.lua | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 603409c..8791a96 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -292,6 +292,8 @@ end local function reset_pan_if_visible() local dim = std.getDimOSD(); if not dim then return end + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins local cmd = "" if (mw >= 0) then cmd = cmd.."no-osd set video-pan-x 0"..";" end if (mh >= 0) then cmd = cmd.."no-osd set video-pan-y 0"..";" end From e5a14aadbf2949b0ad7d07ac32494d07be5e8131 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 22:13:56 +0700 Subject: [PATCH 30/81] image position: fix off-by-one error --- scripts/mpvi/2_image_positioning.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 8791a96..a7269de 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -218,7 +218,7 @@ local function cursor_centric_zoom_handler(amt) mp.command("no-osd set video-zoom "..zoom_origin + zoom_inc.."; no-osd set video-pan-x "..clamp(new_pan_x,-3,3).."; no-osd set video-pan-y "..clamp(new_pan_y,-3,3)) end -local track_count = 0 +local track_count = 1 local track_count_max = 2 -- track first 2 OSD changes on launch until this is implemented https://github.com/mpv-player/mpv/issues/11191 local isOSD = false local align_border_init_x = nil From 7baa3e203136caa3e005f44f8417faad0c742fa8 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 22:14:24 +0700 Subject: [PATCH 31/81] image pos: add timeout in addition to count limit --- scripts/mpvi/2_image_positioning.lua | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index a7269de..78c6ce6 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -220,6 +220,7 @@ end local track_count = 1 local track_count_max = 2 -- track first 2 OSD changes on launch until this is implemented https://github.com/mpv-player/mpv/issues/11191 +local track_timeout = 1 -- but only withint the first 1 second (to avoid swallowing user events) local isOSD = false local align_border_init_x = nil local align_border_init_y = nil @@ -256,6 +257,9 @@ local function align_border_wait_osd() else track_count = track_count + 1 -- or not yet, +count and +align_border align_border(align_border_init_x, align_border_init_y) end end end +local function align_border_wait_osd_timeout() + track_count = track_count_max + 1 +end local function pan_image(axis, amount, zoom_invariant, image_constrained) amount = tonumber(amount) @@ -301,6 +305,7 @@ local function reset_pan_if_visible() end mp.observe_property("osd-dimensions",nil,align_border_wait_osd) -- wait for OSD before aligning border +mp.add_timeout (track_timeout ,align_border_wait_osd_timeout) -- limit waiting time 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}) From 03fe81b64b6cb15fcf6a7c588e969346419870c8 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 21 Jan 2023 22:14:33 +0700 Subject: [PATCH 32/81] image pos: reformat --- scripts/mpvi/2_image_positioning.lua | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 78c6ce6..49e172e 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -246,15 +246,11 @@ end local function align_border_wait_osd() local dim = std.getDimOSD(); if not dim then return end isOSD = true - - if align_border_init_x == nil and align_border_init_y == nil then -- OSD+ align_border− not called - if track_count > track_count_max then -- unregister self if called a few times - mp.unobserve_property( align_border_wait_osd) - else track_count = track_count + 1 end -- or not yet, increase the count - else -- OSD+ align_border+ called - if track_count > track_count_max then -- unregister self if called a few times - mp.unobserve_property( align_border_wait_osd) - else track_count = track_count + 1 -- or not yet, +count and +align_border + if track_count > track_count_max then -- unregister self if called a few times + mp.unobserve_property( align_border_wait_osd) + else + track_count = track_count + 1 -- or not yet, increase the count + if not (align_border_init_x == nil and align_border_init_y == nil) then -- restore align_border call align_border(align_border_init_x, align_border_init_y) end end end local function align_border_wait_osd_timeout() @@ -304,7 +300,7 @@ local function reset_pan_if_visible() if cmd ~= "" then mp.command(cmd) end end -mp.observe_property("osd-dimensions",nil,align_border_wait_osd) -- wait for OSD before aligning border +mp.observe_property("osd-dimensions",nil,align_border_wait_osd ) -- wait for OSD before aligning border mp.add_timeout (track_timeout ,align_border_wait_osd_timeout) -- limit waiting time mp.add_key_binding(nil, "drag-to-pan" , drag_to_pan_handler , {complex = true}) From b538a99b2b631d8ebdeafbcd78e1bcee98878655 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 00:08:48 +0700 Subject: [PATCH 33/81] add observe_print to std --- scripts/mpvi/lib/std.lua | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 954c07e..c12de07 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -51,6 +51,13 @@ function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height return dim, ww, wh end +function std.observe_print(propname) -- notify on changes, use example ↓ + -- local prop_list = {'prop1', 'prop2'} + -- for k, v in pairs(prop_list) do std.observe_print(v) end + mp.observe_property(propname, "string", function(_, val) + mp.msg.info(tostring(val).." \t ←Δ "..propname) + end) +end return { std = std, From a0b65e3347e974a259b247edeacdb30ffcce61f0 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 00:29:05 +0700 Subject: [PATCH 34/81] image position: replace pan with align in align function --- scripts/mpvi/2_image_positioning.lua | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 49e172e..2bfd17d 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -233,14 +233,10 @@ local function align_border(x, y) end return end - local mw = dim.ml + dim.mr -- left+right margins - local mh = dim.mt + dim.mb -- top +bottom margins - local vid_w = ww - mw - local vid_h = wh - mh - local x,y = tonumber(x),tonumber(y) - local cmd = "" - if x then x = clamp( x*(mw/2)/vid_w,-3,3); cmd = cmd.."no-osd set video-pan-x "..x..";" end - if y then y = clamp(-y*(mh/2)/vid_h,-3,3); cmd = cmd.."no-osd set video-pan-y "..y..";" end + local x,y = tonumber(x),tonumber(y) + local cmd = "" + if x then cmd = cmd.."no-osd set video-align-x "..clamp( x,-1,1)..";" end + if y then cmd = cmd.."no-osd set video-align-y "..clamp(-y,-1,1)..";" end if cmd ~= "" then mp.command(cmd) end end local function align_border_wait_osd() From 976b0e1af142a1c170664dfb1b380edd8b12a269 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 00:09:23 +0700 Subject: [PATCH 35/81] image position: reformat --- scripts/mpvi/2_image_positioning.lua | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 2bfd17d..20c52e0 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -24,14 +24,13 @@ local opts = { cursor_centric_zoom_auto_center = true, cursor_centric_zoom_dezoom_if_full_view = false, } -local options = require 'mp.options' -local msg = require 'mp.msg' -local assdraw = require 'mp.assdraw' -local pan_min = -3.0 -local pan_max = 3.0 --- {"video-pan-x", OPT_FLOAT(pan_x), M_RANGE(-3.0, 3.0)}, --- {"video-pan-y", OPT_FLOAT(pan_y), M_RANGE(-3.0, 3.0)}, --- https://github.com/mpv-player/mpv/blob/67dbe2a8f46926951af01a4ac91937f283898bb2/options/options.c#L141 +local options = require 'mp.options' +local msg = require 'mp.msg' +local assdraw = require 'mp.assdraw' +local pan_min = -3.0 -- {"video-pan-x" , OPT_FLOAT(pan_x ), M_RANGE(-3.0, 3.0)}, +local pan_max = 3.0 -- https://github.com/mpv-player/mpv/blob/67dbe2a8f46926951af01a4ac91937f283898bb2/options/options.c#L141 +local align_min = -1.0 -- {"video-align-x", OPT_FLOAT(align_x), M_RANGE(-1.0, 1.0)}, +local align_max = 1.0 -- https://github.com/mpv-player/mpv/blob/67dbe2a8f46926951af01a4ac91937f283898bb2/options/options.c#L143 options.read_options(opts, opt_path_rel, function() end) -- msg.info("drag_to_pan_margin = " .. tostring(opts.drag_to_pan_margin)) From d39d7f2f2965d6915554a701975e2c3c099938ba Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:34:07 +0700 Subject: [PATCH 36/81] =?UTF-8?q?image=20pos:=20make=20vertical=20scale=20?= =?UTF-8?q?to=20+=E2=86=91=20and=20=E2=88=92=E2=86=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/mpvi/2_image_positioning.lua | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 20c52e0..18ea660 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -255,6 +255,7 @@ end local 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 axis == "y" then amount = -1 * amount end -- make +↑ −↓ if zoom_invariant == "yes" then amount = amount / 2 ^ mp.get_property_number("video-zoom") end @@ -267,8 +268,8 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) local margin = (axis == "x" and amount > 0) and dim.ml or (axis == "x" and amount < 0) and dim.mr - or ( amount > 0) and dim.mt - or ( amount < 0) and dim.mb + or (axis == "y" and amount < 0) and dim.mt -- ↑ when moving ↑ (+↓ −↑ (reverse) in video-pan-) + or (axis == "y" and amount > 0) and dim.mb -- ↓ when moving ↓ local vid_size = (axis == "x") and (ww - mw) or (wh - mh) local pixels_moved = math.abs(amount) * vid_size if margin >= 0 then return -- the margin is already visible, no point going further From 7972dafaa5238df4bc762bd87f7b3afac9bbf030 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:36:01 +0700 Subject: [PATCH 37/81] image pos: don't move the picture if it's fully outside --- scripts/mpvi/2_image_positioning.lua | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 18ea660..3386dbc 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -261,10 +261,19 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) end local prop = "video-pan-" .. axis local old_pan = mp.get_property_number(prop) + + local dim, ww, wh = std.getDimOSD(); if not dim then return end + local mw = dim.ml + dim.mr -- left+right margins + local mh = dim.mt + dim.mb -- top +bottom margins + + local margin_visible = -- visible Img area + (axis == "x" and amount > 0) and (ww - dim.ml) -- ← when moving → + or (axis == "x" and amount < 0) and (ww - dim.mr) -- → when moving ← + or (axis == "y" and amount < 0) and (wh - dim.mb) -- ↓ when moving ↑ + or (axis == "y" and amount > 0) and (wh - dim.mt) -- ↑ when moving ↓ + if margin_visible <=0 then return end -- stop if Img is completely outside the Win + if image_constrained == "yes" then - local dim, ww, wh = std.getDimOSD(); if not dim then return end - local mw = dim.ml + dim.mr -- left+right margins - local mh = dim.mt + dim.mb -- top +bottom margins local margin = (axis == "x" and amount > 0) and dim.ml or (axis == "x" and amount < 0) and dim.mr From 410ee55d34eebc10c6597dcfc3db330fd5dbeb54 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:34:54 +0700 Subject: [PATCH 38/81] image pos: update input config with more 'natural' panning directions --- input.conf | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/input.conf b/input.conf index 94ed5b7..47e06cb 100644 --- a/input.conf +++ b/input.conf @@ -38,31 +38,31 @@ WHEEL_DOWN script-message cursor-centric-zoom -0.1 # if yes, will pan by the same if yes, stops panning if the image # amount regardless of zoom would go outside of the window -ctrl+down repeatable script-message pan-image y -0.1 yes yes +ctrl+left repeatable script-message pan-image x -0.1 yes yes +ctrl+right repeatable script-message pan-image x +0.1 yes yes ctrl+up repeatable script-message pan-image y +0.1 yes yes -ctrl+right repeatable script-message pan-image x -0.1 yes yes -ctrl+left repeatable script-message pan-image x +0.1 yes yes +ctrl+down repeatable script-message pan-image y -0.1 yes yes # now with more precision -alt+down repeatable script-message pan-image y -0.01 yes yes +alt+left repeatable script-message pan-image x -0.01 yes yes +alt+right repeatable script-message pan-image x +0.01 yes yes alt+up repeatable script-message pan-image y +0.01 yes yes -alt+right repeatable script-message pan-image x -0.01 yes yes -alt+left repeatable script-message pan-image x +0.01 yes yes +alt+down repeatable script-message pan-image y -0.01 yes yes # replace at will with h,j,k,l if you prefer vim-style bindings # on a trackpad you may want to use these +#WHEEL_LEFT repeatable script-message pan-image x +0.02 yes yes +#WHEEL_RIGHT repeatable script-message pan-image x -0.02 yes yes #WHEEL_UP repeatable script-message pan-image y -0.02 yes yes #WHEEL_DOWN repeatable script-message pan-image y +0.02 yes yes -#WHEEL_LEFT repeatable script-message pan-image x -0.02 yes yes -#WHEEL_RIGHT repeatable script-message pan-image x +0.02 yes yes # align the border of the image to the border of the window # align-border takes the following arguments: # align-border ALIGN_X ALIGN_Y # any value for ALIGN_* is accepted, -1 and 1 map to the border of the window -ctrl+shift+right script-message align-border 1 "" ctrl+shift+left script-message align-border -1 "" +ctrl+shift+right script-message align-border 1 "" ctrl+shift+up script-message align-border "" 1 ctrl+shift+down script-message align-border "" -1 From 7ef0cece1ea493771667b1c1af747239b9c1b592 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:35:07 +0700 Subject: [PATCH 39/81] image pos: reformat --- scripts/mpvi/2_image_positioning.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 3386dbc..ce5146b 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -274,9 +274,9 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) if margin_visible <=0 then return end -- stop if Img is completely outside the Win if image_constrained == "yes" then - local margin = - (axis == "x" and amount > 0) and dim.ml - or (axis == "x" and amount < 0) and dim.mr + local margin = -- empty Win area between Win and Img borders + (axis == "x" and amount > 0) and dim.mr -- → when moving → + or (axis == "x" and amount < 0) and dim.ml -- ← when moving ← or (axis == "y" and amount < 0) and dim.mt -- ↑ when moving ↑ (+↓ −↑ (reverse) in video-pan-) or (axis == "y" and amount > 0) and dim.mb -- ↓ when moving ↓ local vid_size = (axis == "x") and (ww - mw) or (wh - mh) From e5bcbccc8c192f41c27c3cf09471443164de2b41 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:35:28 +0700 Subject: [PATCH 40/81] image pos: fix non-working margins --- scripts/mpvi/2_image_positioning.lua | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index ce5146b..09437be 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -281,9 +281,9 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) or (axis == "y" and amount > 0) and dim.mb -- ↓ when moving ↓ local vid_size = (axis == "x") and (ww - mw) or (wh - mh) local pixels_moved = math.abs(amount) * vid_size - if margin >= 0 then return -- the margin is already visible, no point going further - elseif margin + pixels_moved > 0 then - amount = -(math.abs(amount) / amount) * margin / vid_size + if margin <=0 then return -- no margin, no point going further + elseif margin - pixels_moved < 0 then -- not enough margin, stop at 0 margin + amount = (math.abs(amount)/amount) * margin / vid_size -- todo: partial bug fix for -1px overshoots. Maybe it's some mpv bug that processes events too fast??? end end mp.set_property_number(prop, old_pan + amount) From 41e0ace9889d7da6741be3b5e76b127d6be8e573 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sun, 22 Jan 2023 16:35:45 +0700 Subject: [PATCH 41/81] image pos: partial fix for overshoots Helps with 1pixel overshoots, but not with bigger ones --- scripts/mpvi/2_image_positioning.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/2_image_positioning.lua index 09437be..972369a 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/2_image_positioning.lua @@ -283,7 +283,7 @@ local function pan_image(axis, amount, zoom_invariant, image_constrained) local pixels_moved = math.abs(amount) * vid_size if margin <=0 then return -- no margin, no point going further elseif margin - pixels_moved < 0 then -- not enough margin, stop at 0 margin - amount = (math.abs(amount)/amount) * margin / vid_size -- todo: partial bug fix for -1px overshoots. Maybe it's some mpv bug that processes events too fast??? + amount = (math.abs(amount)/amount) * (margin - 0.5) / vid_size -- todo: partial bug fix for -1px overshoots. Maybe it's some mpv bug that processes events too fast??? end end mp.set_property_number(prop, old_pan + amount) From 61fdec0dbd6d9f85593cd754904a3cae44ea520a Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:30:15 +0700 Subject: [PATCH 42/81] std: escape magic chars in string match patterns --- scripts/mpvi/lib/std.lua | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index c12de07..6c5d4f3 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -1,8 +1,10 @@ local std = {} -- stdlib -string.startswith = function(self, str) return self:find( '^' .. str ) ~= nil end -string.endswith = function(self, str) return self:find( str .. '$') ~= nil end -string.trim = function(self ) return self:match("^%s*(.-)%s*$" ) end +-- escape magic chars ^$()%.[]*+-? https://www.lua.org/manual/5.1/manual.html#5.4.1 +string.escmagic = function(self, str) return self:gsub("([^%w])", "%%%1") end +string.startswith = function(self, str) return self:find('^' .. str:escmagic() ) ~= nil end +string.endswith = function(self, str) return self:find( str:escmagic() .. '$') ~= nil end +string.trim = function(self ) return self:match("^%s*(.-)%s*$") end function std.getScriptFullPath() local source = debug.getinfo(2,"S").source From a945de78a46e46b8f02131c63b1531a34f8a5013 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:31:02 +0700 Subject: [PATCH 43/81] std: add simple table print function --- scripts/mpvi/lib/std.lua | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 6c5d4f3..8637348 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -43,6 +43,16 @@ function std.basename(path) -- get filename from path return path:gsub("(.*[/\\])(.*)" ,"%2") -- (path/)(file) → (file) end +-- Print contents of `tbl`, with indentation `indent` gist.github.com/ripter/4270799 +function std.tprint(tbl, indent) + if not indent then indent = 0 end + for k, v in pairs(tbl) do + formatting = string.rep(" ", indent) .. k .. ": " + if type(v) == "table" then print(formatting ); std.tprint(v,indent+1) + elseif type(v) == 'boolean' then print(formatting .. tostring(v)) + else print(formatting .. v ) end + end +end -- MPV-specific functions function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height positive local dim = mp.get_property_native("osd-dimensions") From 1258cae9d799bff7bfd597470d43f85b69468aec Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:32:25 +0700 Subject: [PATCH 44/81] std: add table length function --- scripts/mpvi/lib/std.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 8637348..599cd70 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -53,6 +53,12 @@ function std.tprint(tbl, indent) else print(formatting .. v ) end end end + +function std.tlen(T) + local count = 0 + for _ in pairs(T) do count = count + 1 end + return count +end -- MPV-specific functions function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height positive local dim = mp.get_property_native("osd-dimensions") From 8965f48c45fdde3e5dde149dcc7b3a03f8922442 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:33:05 +0700 Subject: [PATCH 45/81] std: add Math round function --- scripts/mpvi/lib/std.lua | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 599cd70..b2377cd 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -59,6 +59,24 @@ function std.tlen(T) for _ in pairs(T) do count = count + 1 end return count end + +-- Math +-- Round @ https://stackoverflow.com/a/58411671 +function std.round(num) + -- if Lua uses double precision IEC-559 (aka IEEE-754) floats (most do) + -- if -2⁵¹ < num < 2⁵¹ + -- rounding using your FPU's current rounding mode, which is usually round to nearest, ties to even + return num + (2^52 + 2^51) - (2^52 + 2^51) +end +function std.roundp(num) -- less efficient, performs the same FPU rounding but works for all numbers + -- doesn't seem to differ, still fails at 0.5=0 + local ofs = 2^52 + if math.abs(num) > ofs then + return num + end + return num < 0 and num - ofs + ofs or num + ofs - ofs +end + -- MPV-specific functions function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height positive local dim = mp.get_property_native("osd-dimensions") From b3f8526ab2ef4202533c9efec751e79d267a4ca9 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:33:30 +0700 Subject: [PATCH 46/81] std: import MPV options,msg --- scripts/mpvi/lib/std.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index b2377cd..96af311 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -78,6 +78,9 @@ function std.roundp(num) -- less efficient, performs the same FPU rounding but w end -- MPV-specific functions +local options = require 'mp.options' +local msg = require 'mp.msg' + function std.getDimOSD() -- get OSD dimensions only if they exist and wid/height positive local dim = mp.get_property_native("osd-dimensions") if not dim then return nil, nil, nil end @@ -91,7 +94,7 @@ function std.observe_print(propname) -- notify on changes, use example ↓ -- local prop_list = {'prop1', 'prop2'} -- for k, v in pairs(prop_list) do std.observe_print(v) end mp.observe_property(propname, "string", function(_, val) - mp.msg.info(tostring(val).." \t ←Δ "..propname) + msg.info(tostring(val).." \t ←Δ "..propname) end) end From c8da0770f7cc4d768c7faf83d5846c24533b58bf Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:34:55 +0700 Subject: [PATCH 47/81] std: add YAML config importer --- scripts/mpvi/lib/std.lua | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 96af311..82337b7 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -98,6 +98,116 @@ function std.observe_print(propname) -- notify on changes, use example ↓ end) end +local parse_yaml = require "lib/tinyyaml".parse +function std.read_options_yaml(opts, identifier, on_update) + if identifier == nil then identifier = mp.get_script_name() end + msg.debug("reading options for "..identifier) + + -- get, read, and parse YAML config file + local function read_yaml_config(identifier) -- get YAML config file contet + local yaml_ext = {'yml','yaml'} + local cfgContent,err,cfgF = nil,"",nil + for k, ext in pairs(yaml_ext) do + local cfgFileName = identifier.."."..ext + local cfgFilePath = "script-opts".."/"..cfgFileName + cfgF = mp.find_config_file(cfgFilePath) + if cfgF == nil then err = err.."failed to find '"..cfgFilePath.."'" ..'\n' + else + local f, e = io.open(cfgF,"r") + if f == nil then err = err.."failed to open '"..cfgF.."' error: "..e..'\n' + else cfgContent = f:read("*all"); io.close(f) end + end + end + if err then msg.debug(err) end + return cfgContent, cfgF + end + + local k_prev,k_top = "","" -- defined outside of ↓ to store recursive compound strings of previous keys + local opt_change = {} + local function import_opt(cfg_def, cfg_act, cfg_src, change) -- merge old and new configs + if change and type(change) ~= 'table' then change = nil + local warn = "[ignore] wrong argument type: expected 'table'".. + ", got '"..type(change).. + "\n in 'import_opt(...change)' function argument" + msg.warn(warn) + end + local i = 0 + local k_def,k_first = nil + local def_len = std.tlen(cfg_def) + local act_len = std.tlen(cfg_act) + for k, v in pairs(cfg_act) do + i = i + 1 + if i == 1 then k_first = k end -- store 1st key to read types from cfg_def if it only has 1 key + if (def_len >= act_len) then k_def = k + elseif (def_len > 1 ) then k_def = k + else k_def = k_first end + if cfg_def[k_def] == nil then + local warn = "[ignore] unknown key '"..k_prev..k.."' = "..tostring(v) + if cfg_src then warn = warn.."\n in '"..cfg_src.."'" end + msg.warn(warn) + else + local type_def = type(cfg_def[k_def]) + local type_cfg = type(v) + if type_def ~= type_cfg then -- mismatch + local warn = "[ignore] wrong key type: ".. + "expected '"..type_def.. + "', got '" ..type_cfg.. + "' in key '"..k_prev..k.."' = "..tostring(v) + if cfg_src then warn = warn.."\n in '" ..cfg_src.."'" end + msg.warn(warn) + elseif type_def == 'table' then -- → recursively check tables + k_prev = k_prev..k.."'→'" + if change then + change[k] = v -- allows setting change status to leaf keys ↓ + import_opt(cfg_def[k], cfg_act[k], cfg_src, change[k]) + else import_opt(cfg_def[k], cfg_act[k], cfg_src ) end + else -- or replace config value with parsed + if cfg_def[k] ~= v then -- avoids copying + cfg_def[k] = v + if change then change[k] = true end -- set change status to leaf keys + else + if change then change[k] = false end + end + end + end + end + k_prev = "" + end + + -- 1. Import YAML config + local cfg_yaml, cfg_file = read_yaml_config(identifier) -- 1a. read yaml config + local cfg_parsed = nil + local function try_parse_yaml() cfg_parsed = parse_yaml(cfg_yaml) end -- pcall accepts fn, not fn(arg) + if cfg_yaml ~= nil and not pcall(try_parse_yaml) then msg.warn("failed to parse YAML config") + else import_opt(opts, cfg_parsed, cfg_file) end -- 1b. merge yaml config + + -- 2. Import CLI config (overrides 1.) + local cfg_src = 'command line' + local cfg_parsed = nil + local cli_prefix = identifier.."-" + local cli_prefix_re = cli_prefix:escmagic() + local cfg_cli_raw = mp.get_property_native("options/script-opts") -- 2a. read cli opt + local function parse_prefix_cfg(cfg, cfg_src) + local cfg_cli_parsed = {} + for k, v in pairs(cfg) do + if k:startswith(cli_prefix) then cfg_cli_parsed[k:gsub(cli_prefix_re,'',1)]=v end end + return cfg_cli_parsed + end + if next(cfg_cli_raw) ~= nil then cfg_parsed = parse_prefix_cfg(cfg_cli_raw, cfg_src) + if next(cfg_parsed ) ~= nil then import_opt(opts, cfg_parsed, cfg_src) end end -- 2b. merge cli opt + + -- 3. Register auto-update function + if on_update then mp.observe_property("options/script-opts", "native", function(name, opt_new) + local cfg_src = 'on_update' + local cfg_parsed = nil + local opt_change = {} + if next(opt_new ) ~= nil then cfg_parsed = parse_prefix_cfg(opt_new,cfg_src) -- 3a. merge new opt + if next(cfg_parsed) ~= nil then import_opt(opts, cfg_parsed, cfg_src, opt_change) end end + if next(opt_change) ~= nil then on_update(opt_change) end + end) + end +end + return { std = std, } From 46c881d87e6e1df7f392b9d34b03eef3f52e4d87 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:42:40 +0700 Subject: [PATCH 48/81] lib: add YAML parser --- scripts/mpvi/lib/tinyyaml | 21 + scripts/mpvi/lib/tinyyaml.lua | 849 ++++++++++++++++++++++++++++++++++ 2 files changed, 870 insertions(+) create mode 100644 scripts/mpvi/lib/tinyyaml create mode 100644 scripts/mpvi/lib/tinyyaml.lua diff --git a/scripts/mpvi/lib/tinyyaml b/scripts/mpvi/lib/tinyyaml new file mode 100644 index 0000000..12869b0 --- /dev/null +++ b/scripts/mpvi/lib/tinyyaml @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 peposso + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/scripts/mpvi/lib/tinyyaml.lua b/scripts/mpvi/lib/tinyyaml.lua new file mode 100644 index 0000000..77ab685 --- /dev/null +++ b/scripts/mpvi/lib/tinyyaml.lua @@ -0,0 +1,849 @@ +------------------------------------------------------------------------------- +-- tinyyaml - YAML subset parser +------------------------------------------------------------------------------- + +local table = table +local string = string +local schar = string.char +local ssub, gsub = string.sub, string.gsub +local sfind, smatch = string.find, string.match +local tinsert, tconcat, tremove = table.insert, table.concat, table.remove +local setmetatable = setmetatable +local pairs = pairs +local rawget = rawget +local type = type +local tonumber = tonumber +local math = math +local getmetatable = getmetatable +local error = error +local end_symbol = "..." +local end_break_symbol = "...\n" + +local UNESCAPES = { + ['0'] = "\x00", z = "\x00", N = "\x85", + a = "\x07", b = "\x08", t = "\x09", + n = "\x0a", v = "\x0b", f = "\x0c", + r = "\x0d", e = "\x1b", ['\\'] = '\\', +}; + +------------------------------------------------------------------------------- +-- utils +local function select(list, pred) + local selected = {} + for i = 0, #list do + local v = list[i] + if v and pred(v, i) then + tinsert(selected, v) + end + end + return selected +end + +local function startswith(haystack, needle) + return ssub(haystack, 1, #needle) == needle +end + +local function ltrim(str) + return smatch(str, "^%s*(.-)$") +end + +local function rtrim(str) + return smatch(str, "^(.-)%s*$") +end + +local function trim(str) + return smatch(str, "^%s*(.-)%s*$") +end + +------------------------------------------------------------------------------- +-- Implementation. +-- +local class = {__meta={}} +function class.__meta.__call(cls, ...) + local self = setmetatable({}, cls) + if cls.__init then + cls.__init(self, ...) + end + return self +end + +function class.def(base, typ, cls) + base = base or class + local mt = {__metatable=base, __index=base} + for k, v in pairs(base.__meta) do mt[k] = v end + cls = setmetatable(cls or {}, mt) + cls.__index = cls + cls.__metatable = cls + cls.__type = typ + cls.__meta = mt + return cls +end + + +local types = { + null = class:def('null'), + map = class:def('map'), + omap = class:def('omap'), + pairs = class:def('pairs'), + set = class:def('set'), + seq = class:def('seq'), + timestamp = class:def('timestamp'), +} + +local Null = types.null +function Null.__tostring() return 'yaml.null' end +function Null.isnull(v) + if v == nil then return true end + if type(v) == 'table' and getmetatable(v) == Null then return true end + return false +end +local null = Null() + +function types.timestamp:__init(y, m, d, h, i, s, f, z) + self.year = tonumber(y) + self.month = tonumber(m) + self.day = tonumber(d) + self.hour = tonumber(h or 0) + self.minute = tonumber(i or 0) + self.second = tonumber(s or 0) + if type(f) == 'string' and sfind(f, '^%d+$') then + self.fraction = tonumber(f) * math.pow(10, 3 - #f) + elseif f then + self.fraction = f + else + self.fraction = 0 + end + self.timezone = z +end + +function types.timestamp:__tostring() + return string.format( + '%04d-%02d-%02dT%02d:%02d:%02d.%03d%s', + self.year, self.month, self.day, + self.hour, self.minute, self.second, self.fraction, + self:gettz()) +end + +function types.timestamp:gettz() + if not self.timezone then + return '' + end + if self.timezone == 0 then + return 'Z' + end + local sign = self.timezone > 0 + local z = sign and self.timezone or -self.timezone + local zh = math.floor(z) + local zi = (z - zh) * 60 + return string.format( + '%s%02d:%02d', sign and '+' or '-', zh, zi) +end + + +local function countindent(line) + local _, j = sfind(line, '^%s+') + if not j then + return 0, line + end + return j, ssub(line, j+1) +end + +local Parser = { + timestamps=true,-- parse timestamps as objects instead of strings +} + +function Parser:parsestring(line, stopper) + stopper = stopper or '' + local q = ssub(line, 1, 1) + if q == ' ' or q == '\t' then + return self:parsestring(ssub(line, 2)) + end + if q == "'" then + local i = sfind(line, "'", 2, true) + if not i then + return nil, line + end + return ssub(line, 2, i-1), ssub(line, i+1) + end + if q == '"' then + local i, buf = 2, '' + while i < #line do + local c = ssub(line, i, i) + if c == '\\' then + local n = ssub(line, i+1, i+1) + if UNESCAPES[n] ~= nil then + buf = buf..UNESCAPES[n] + elseif n == 'x' then + local h = ssub(i+2,i+3) + if sfind(h, '^[0-9a-fA-F]$') then + buf = buf..schar(tonumber(h, 16)) + i = i + 2 + else + buf = buf..'x' + end + else + buf = buf..n + end + i = i + 1 + elseif c == q then + break + else + buf = buf..c + end + i = i + 1 + end + return buf, ssub(line, i+1) + end + if q == '{' or q == '[' then -- flow style + return nil, line + end + if q == '|' or q == '>' then -- block + return nil, line + end + if q == '-' or q == ':' then + if ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1 then + return nil, line + end + end + + if line == "*" then + error("did not find expected alphabetic or numeric character") + end + + local buf = '' + while #line > 0 do + local c = ssub(line, 1, 1) + if sfind(stopper, c, 1, true) then + break + elseif c == ':' and (ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1) then + break + elseif c == '#' and (ssub(buf, #buf, #buf) == ' ') then + break + else + buf = buf..c + end + line = ssub(line, 2) + end + return rtrim(buf), line +end + +local function isemptyline(line) + return line == '' or sfind(line, '^%s*$') or sfind(line, '^%s*#') +end + +local function equalsline(line, needle) + return startswith(line, needle) and isemptyline(ssub(line, #needle+1)) +end + +local function compactifyemptylines(lines) + -- Appends empty lines as "\n" to the end of the nearest preceding non-empty line + local compactified = {} + local lastline = {} + for i = 1, #lines do + local line = lines[i] + if isemptyline(line) then + if #compactified > 0 and i < #lines then + tinsert(lastline, "\n") + end + else + if #lastline > 0 then + tinsert(compactified, tconcat(lastline, "")) + end + lastline = {line} + end + end + if #lastline > 0 then + tinsert(compactified, tconcat(lastline, "")) + end + return compactified +end + +local function checkdupekey(map, key) + if rawget(map, key) ~= nil then + -- print("found a duplicate key '"..key.."' in line: "..line) + local suffix = 1 + while rawget(map, key..'_'..suffix) do + suffix = suffix + 1 + end + key = key ..'_'..suffix + end + return key +end + + +function Parser:parseflowstyle(line, lines) + local stack = {} + while true do + if #line == 0 then + if #lines == 0 then + break + else + line = tremove(lines, 1) + end + end + local c = ssub(line, 1, 1) + if c == '#' then + line = '' + elseif c == ' ' or c == '\t' or c == '\r' or c == '\n' then + line = ssub(line, 2) + elseif c == '{' or c == '[' then + tinsert(stack, {v={},t=c}) + line = ssub(line, 2) + elseif c == ':' then + local s = tremove(stack) + tinsert(stack, {v=s.v, t=':'}) + line = ssub(line, 2) + elseif c == ',' then + local value = tremove(stack) + if value.t == ':' or value.t == '{' or value.t == '[' then error() end + if stack[#stack].t == ':' then + -- map + local key = tremove(stack) + key.v = checkdupekey(stack[#stack].v, key.v) + stack[#stack].v[key.v] = value.v + elseif stack[#stack].t == '{' then + -- set + stack[#stack].v[value.v] = true + elseif stack[#stack].t == '[' then + -- seq + tinsert(stack[#stack].v, value.v) + end + line = ssub(line, 2) + elseif c == '}' then + if stack[#stack].t == '{' then + if #stack == 1 then break end + stack[#stack].t = '}' + line = ssub(line, 2) + else + line = ','..line + end + elseif c == ']' then + if stack[#stack].t == '[' then + if #stack == 1 then break end + stack[#stack].t = ']' + line = ssub(line, 2) + else + line = ','..line + end + else + local s, rest = self:parsestring(line, ',{}[]') + if not s then + error('invalid flowstyle line: '..line) + end + tinsert(stack, {v=s, t='s'}) + line = rest + end + end + return stack[1].v, line +end + +function Parser:parseblockstylestring(line, lines, indent) + if #lines == 0 then + error("failed to find multi-line scalar content") + end + local s = {} + local firstindent = -1 + local endline = -1 + for i = 1, #lines do + local ln = lines[i] + local idt = countindent(ln) + if idt <= indent then + break + end + if ln == '' then + tinsert(s, '') + else + if firstindent == -1 then + firstindent = idt + elseif idt < firstindent then + break + end + tinsert(s, ssub(ln, firstindent + 1)) + end + endline = i + end + + local striptrailing = true + local sep = '\n' + local newlineatend = true + if line == '|' then + striptrailing = true + sep = '\n' + newlineatend = true + elseif line == '|+' then + striptrailing = false + sep = '\n' + newlineatend = true + elseif line == '|-' then + striptrailing = true + sep = '\n' + newlineatend = false + elseif line == '>' then + striptrailing = true + sep = ' ' + newlineatend = true + elseif line == '>+' then + striptrailing = false + sep = ' ' + newlineatend = true + elseif line == '>-' then + striptrailing = true + sep = ' ' + newlineatend = false + else + error('invalid blockstyle string:'..line) + end + + if #s == 0 then + return "" + end + + local _, eonl = s[#s]:gsub('\n', '\n') + s[#s] = rtrim(s[#s]) + if striptrailing then + eonl = 0 + end + if newlineatend then + eonl = eonl + 1 + end + for i = endline, 1, -1 do + tremove(lines, i) + end + return tconcat(s, sep)..string.rep('\n', eonl) +end + +function Parser:parsetimestamp(line) + local _, p1, y, m, d = sfind(line, '^(%d%d%d%d)%-(%d%d)%-(%d%d)') + if not p1 then + return nil, line + end + if p1 == #line then + return types.timestamp(y, m, d), '' + end + local _, p2, h, i, s = sfind(line, '^[Tt ](%d+):(%d+):(%d+)', p1+1) + if not p2 then + return types.timestamp(y, m, d), ssub(line, p1+1) + end + if p2 == #line then + return types.timestamp(y, m, d, h, i, s), '' + end + local _, p3, f = sfind(line, '^%.(%d+)', p2+1) + if not p3 then + p3 = p2 + f = 0 + end + local zc = ssub(line, p3+1, p3+1) + local _, p4, zs, z = sfind(line, '^ ?([%+%-])(%d+)', p3+1) + if p4 then + z = tonumber(z) + local _, p5, zi = sfind(line, '^:(%d+)', p4+1) + if p5 then + z = z + tonumber(zi) / 60 + end + z = zs == '-' and -tonumber(z) or tonumber(z) + elseif zc == 'Z' then + p4 = p3 + 1 + z = 0 + else + p4 = p3 + z = false + end + return types.timestamp(y, m, d, h, i, s, f, z), ssub(line, p4+1) +end + +function Parser:parsescalar(line, lines, indent) + line = trim(line) + line = gsub(line, '^%s*#.*$', '') -- comment only -> '' + line = gsub(line, '^%s*', '') -- trim head spaces + + if line == '' or line == '~' then + return null + end + + if self.timestamps then + local ts, _ = self:parsetimestamp(line) + if ts then + return ts + end + end + + local s, _ = self:parsestring(line) + -- startswith quote ... string + -- not startswith quote ... maybe string + if s and (startswith(line, '"') or startswith(line, "'")) then + return s + end + + if startswith('!', line) then -- unexpected tagchar + error('unsupported line: '..line) + end + + if equalsline(line, '{}') then + return {} + end + if equalsline(line, '[]') then + return {} + end + + if startswith(line, '{') or startswith(line, '[') then + return self:parseflowstyle(line, lines) + end + + if startswith(line, '|') or startswith(line, '>') then + return self:parseblockstylestring(line, lines, indent) + end + + -- Regular unquoted string + line = gsub(line, '%s*#.*$', '') -- trim tail comment + local v = line + if v == 'null' or v == 'Null' or v == 'NULL'then + return null + elseif v == 'true' or v == 'True' or v == 'TRUE' then + return true + elseif v == 'false' or v == 'False' or v == 'FALSE' then + return false + elseif v == '.inf' or v == '.Inf' or v == '.INF' then + return math.huge + elseif v == '+.inf' or v == '+.Inf' or v == '+.INF' then + return math.huge + elseif v == '-.inf' or v == '-.Inf' or v == '-.INF' then + return -math.huge + elseif v == '.nan' or v == '.NaN' or v == '.NAN' then + return 0 / 0 + elseif sfind(v, '^[%+%-]?[0-9]+$') or sfind(v, '^[%+%-]?[0-9]+%.$')then + return tonumber(v) -- : int + elseif sfind(v, '^[%+%-]?[0-9]+%.[0-9]+$') then + return tonumber(v) + end + return s or v +end + +function Parser:parseseq(line, lines, indent) + local seq = setmetatable({}, types.seq) + if line ~= '' then + error() + end + while #lines > 0 do + -- Check for a new document + line = lines[1] + if startswith(line, '---') then + while #lines > 0 and not startswith(lines, '---') do + tremove(lines, 1) + end + return seq + end + + -- Check the indent level + local level = countindent(line) + if level < indent then + return seq + elseif level > indent then + error("found bad indenting in line: ".. line) + end + + local i, j = sfind(line, '%-%s+') + if not i then + i, j = sfind(line, '%-$') + if not i then + return seq + end + end + local rest = ssub(line, j+1) + + if sfind(rest, '^[^\'\"%s]*:%s*$') or sfind(rest, '^[^\'\"%s]*:%s+.') then + -- Inline nested hash + -- There are two patterns need to match as inline nested hash + -- first one should have no other characters except whitespace after `:` + -- and the second one should have characters besides whitespace after `:` + -- + -- value: + -- - foo: + -- bar: 1 + -- + -- and + -- + -- value: + -- - foo: bar + -- + -- And there is one pattern should not be matched, where there is no space after `:` + -- in below, `foo:bar` should be parsed into a single string + -- + -- value: + -- - foo:bar + local indent2 = j + lines[1] = string.rep(' ', indent2)..rest + tinsert(seq, self:parsemap('', lines, indent2)) + elseif sfind(rest, '^%-%s+') then + -- Inline nested seq + local indent2 = j + lines[1] = string.rep(' ', indent2)..rest + tinsert(seq, self:parseseq('', lines, indent2)) + elseif isemptyline(rest) then + tremove(lines, 1) + if #lines == 0 then + tinsert(seq, null) + return seq + end + if sfind(lines[1], '^%s*%-') then + local nextline = lines[1] + local indent2 = countindent(nextline) + if indent2 == indent then + -- Null seqay entry + tinsert(seq, null) + else + tinsert(seq, self:parseseq('', lines, indent2)) + end + else + -- - # comment + -- key: value + local nextline = lines[1] + local indent2 = countindent(nextline) + tinsert(seq, self:parsemap('', lines, indent2)) + end + elseif line == "*" then + error("did not find expected alphabetic or numeric character") + elseif rest then + -- Array entry with a value + tremove(lines, 1) + tinsert(seq, self:parsescalar(rest, lines)) + end + end + return seq +end + +function Parser:parseset(line, lines, indent) + if not isemptyline(line) then + error('not seq line: '..line) + end + local set = setmetatable({}, types.set) + while #lines > 0 do + -- Check for a new document + line = lines[1] + if startswith(line, '---') then + while #lines > 0 and not startswith(lines, '---') do + tremove(lines, 1) + end + return set + end + + -- Check the indent level + local level = countindent(line) + if level < indent then + return set + elseif level > indent then + error("found bad indenting in line: ".. line) + end + + local i, j = sfind(line, '%?%s+') + if not i then + i, j = sfind(line, '%?$') + if not i then + return set + end + end + local rest = ssub(line, j+1) + + if sfind(rest, '^[^\'\"%s]*:') then + -- Inline nested hash + local indent2 = j + lines[1] = string.rep(' ', indent2)..rest + set[self:parsemap('', lines, indent2)] = true + elseif sfind(rest, '^%s+$') then + tremove(lines, 1) + if #lines == 0 then + tinsert(set, null) + return set + end + if sfind(lines[1], '^%s*%?') then + local indent2 = countindent(lines[1]) + if indent2 == indent then + -- Null array entry + set[null] = true + else + set[self:parseseq('', lines, indent2)] = true + end + end + + elseif rest then + tremove(lines, 1) + set[self:parsescalar(rest, lines)] = true + else + error("failed to classify line: "..line) + end + end + return set +end + +function Parser:parsemap(line, lines, indent) + if not isemptyline(line) then + error('not map line: '..line) + end + local map = setmetatable({}, types.map) + while #lines > 0 do + -- Check for a new document + line = lines[1] + if line == end_symbol or line == end_break_symbol then + for i, _ in ipairs(lines) do + lines[i] = nil + end + return map + end + + if startswith(line, '---') then + while #lines > 0 and not startswith(lines, '---') do + tremove(lines, 1) + end + return map + end + + -- Check the indent level + local level, _ = countindent(line) + if level < indent then + return map + elseif level > indent then + error("found bad indenting in line: ".. line) + end + + -- Find the key + local key + local s, rest = self:parsestring(line) + + -- Quoted keys + if s and startswith(rest, ':') then + local sc = self:parsescalar(s, {}, 0) + if sc and type(sc) ~= 'string' then + key = sc + else + key = s + end + line = ssub(rest, 2) + else + error("failed to classify line: "..line) + end + + key = checkdupekey(map, key) + line = ltrim(line) + + if ssub(line, 1, 1) == '!' then + -- ignore type + local rh = ltrim(ssub(line, 3)) + local typename = smatch(rh, '^!?[^%s]+') + line = ltrim(ssub(rh, #typename+1)) + end + + if not isemptyline(line) then + tremove(lines, 1) + line = ltrim(line) + map[key] = self:parsescalar(line, lines, indent) + else + -- An indent + tremove(lines, 1) + if #lines == 0 then + map[key] = null + return map; + end + if sfind(lines[1], '^%s*%-') then + local indent2 = countindent(lines[1]) + map[key] = self:parseseq('', lines, indent2) + elseif sfind(lines[1], '^%s*%?') then + local indent2 = countindent(lines[1]) + map[key] = self:parseset('', lines, indent2) + else + local indent2 = countindent(lines[1]) + if indent >= indent2 then + -- Null hash entry + map[key] = null + else + map[key] = self:parsemap('', lines, indent2) + end + end + end + end + return map +end + + +-- : (list)->dict +function Parser:parsedocuments(lines) + lines = compactifyemptylines(lines) + + if sfind(lines[1], '^%%YAML') then tremove(lines, 1) end + + local root = {} + local in_document = false + while #lines > 0 do + local line = lines[1] + -- Do we have a document header? + local docright; + if sfind(line, '^%-%-%-') then + -- Handle scalar documents + docright = ssub(line, 4) + tremove(lines, 1) + in_document = true + end + if docright then + if (not sfind(docright, '^%s+$') and + not sfind(docright, '^%s+#')) then + tinsert(root, self:parsescalar(docright, lines)) + end + elseif #lines == 0 or startswith(line, '---') then + -- A naked document + tinsert(root, null) + while #lines > 0 and not sfind(lines[1], '---') do + tremove(lines, 1) + end + in_document = false + -- XXX The final '-+$' is to look for -- which ends up being an + -- error later. + elseif not in_document and #root > 0 then + -- only the first document can be explicit + error('parse error: '..line) + elseif sfind(line, '^%s*%-') then + -- An array at the root + tinsert(root, self:parseseq('', lines, 0)) + elseif sfind(line, '^%s*[^%s]') then + -- A hash at the root + local level = countindent(line) + tinsert(root, self:parsemap('', lines, level)) + else + -- Shouldn't get here. @lines have whitespace-only lines + -- stripped, and previous match is a line with any + -- non-whitespace. So this clause should only be reachable via + -- a perlbug where \s is not symmetric with \S + + -- uncoverable statement + error('parse error: '..line) + end + end + if #root > 1 and Null.isnull(root[1]) then + tremove(root, 1) + return root + end + return root +end + +--- Parse yaml string into table. +function Parser:parse(source) + local lines = {} + for line in string.gmatch(source .. '\n', '(.-)\r?\n') do + tinsert(lines, line) + end + + local docs = self:parsedocuments(lines) + if #docs == 1 then + return docs[1] + end + + return docs +end + +local function parse(source, options) + local options = options or {} + local parser = setmetatable (options, {__index=Parser}) + return parser:parse(source) +end + +return { + version = 0.1, + parse = parse, +} From 2413c36cd23846d7743315fc986d4e4c1c61def9 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:44:48 +0700 Subject: [PATCH 49/81] detect img: allow tables in run_maybe --- scripts/mpvi/9_detect_image.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/9_detect_image.lua index c95aa6f..426df89 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/9_detect_image.lua @@ -19,8 +19,12 @@ local msg = require 'mp.msg' options.read_options(opts, opt_path_rel, function() end) -local function run_maybe(str) - if str ~= "" then mp.command(str) end +local function run_maybe(str_or_strdict) + local arg_type = type(str_or_strdict) + if arg_type == 'string' then + if str_or_strdict ~= "" then mp.command(str_or_strdict) end + elseif arg_type == 'table' then + for k, v in pairs(str_or_strdict) do run_maybe(v) end end end local wasImg = false From 533bfcaa698224e46c345a8fdbd8cb047c1f65a8 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:45:34 +0700 Subject: [PATCH 50/81] detect img: use YAML config --- scripts/mpvi/9_detect_image.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/9_detect_image.lua index 426df89..a9bae85 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/9_detect_image.lua @@ -1,6 +1,7 @@ -- Detects when 🖼 are loaded and allows you to run commands configured in 1_detect_image.conf -local std = require "lib/std".std +local std = require "lib/std".std +local parse_yaml = require "lib/tinyyaml".parse local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi local script_dir_base = std.basename(script_dir) -- mpvi @@ -10,14 +11,14 @@ local script_stem = std.delua(script_file_name) -- local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ local opts = { - on_load_image_first = "", - on_load_image = "", - on_load_non_image = "", + on_load_image_first = {""}, + on_load_image = {""}, + on_load_non_image = {""}, } local options = require 'mp.options' local msg = require 'mp.msg' -options.read_options(opts, opt_path_rel, function() end) +std.read_options_yaml(opts, opt_path_rel, function() end) local function run_maybe(str_or_strdict) local arg_type = type(str_or_strdict) From f9af0e5a775ad8b5439ca238d56aed713de3c9ed Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:52:43 +0700 Subject: [PATCH 51/81] detect img: rename config --- scripts/mpvi/9_detect_image.lua | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/9_detect_image.lua index a9bae85..a0a5480 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/9_detect_image.lua @@ -13,7 +13,7 @@ local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/ Date: Tue, 24 Jan 2023 15:53:07 +0700 Subject: [PATCH 52/81] detect img: replace config with YAML --- script-opts/mpvi/9_detect_image.conf | 11 ----------- script-opts/mpvi/9_detect_image.yaml | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) delete mode 100644 script-opts/mpvi/9_detect_image.conf create mode 100644 script-opts/mpvi/9_detect_image.yaml diff --git a/script-opts/mpvi/9_detect_image.conf b/script-opts/mpvi/9_detect_image.conf deleted file mode 100644 index b9168d6..0000000 --- a/script-opts/mpvi/9_detect_image.conf +++ /dev/null @@ -1,11 +0,0 @@ -# MUST be 'key= value', no spaces before '=' or not loaded!!! -# commands to execute when a file detected as an image (1 frame, no audio) is loaded or unloaded -# these "hooks" let you change bindings, profiles, reset properties... mpv.io/manual/master/#list-of-input-commands for general command information -# CAN'T "unload a profile", to emulate this you must create an opposite profile and load that - -#on_load_image_first = loaded image, and the previous file was not an image (or there was no previous file) -#on_load_image = loaded image (regardless of what the previous file was) -#on_load_non_image = loaded non-image, and the previous file was an image -on_load_image_first= apply-profile mpvi ; enable-section mpvi; script-message status-line-enable -on_load_image= no-osd set video-pan-x 0; script-message align-border "" -1 -on_load_non_image= apply-profile mpvi restore; disable-section mpvi; no-osd set video-pan-y 0; no-osd set video-pan-x 0; no-osd set video-zoom 0; script-message status-line-disable diff --git a/script-opts/mpvi/9_detect_image.yaml b/script-opts/mpvi/9_detect_image.yaml new file mode 100644 index 0000000..e035a2e --- /dev/null +++ b/script-opts/mpvi/9_detect_image.yaml @@ -0,0 +1,18 @@ +# commands to execute when a file detected as an image (1 frame, no audio) is loaded or unloaded +# these "hooks" let you change bindings, profiles, reset properties... mpv.io/manual/master/#list-of-input-commands for general command information +# CAN'T "unload a profile", to emulate this you must create an opposite profile and load that +# ↓ "quote commands" to avoid lua libyaml parser bug that includes #comments +on_load_image_first : # current file: image, previous: non-image or none + - "apply-profile mpvi" # enable config profile [mpvi] + - "enable-section mpvi" # enable key bindings with {mpvi} named input section + - "script-message status-line-enable" # enable status line (command set in status_line.lua) +on_load_image : # current file: image, previous: any + # - "script-message align-border '' 1" # align image to the top + - "no-osd set video-pan-x 0" # reset x pan +on_unload_image : # current file: non-image, previous: image + - "apply-profile mpvi restore" # try to restore previous config profile + - "disable-section mpvi" # disable key bindings with {mpvi} + - "no-osd set video-pan-x 0" # reset pan/zoom + - "no-osd set video-pan-y 0" # ... + - "no-osd set video-zoom 0" # ... + - "script-message status-line-disable" # disable status line (command set in status_line.lua) From 636723e26deea85e62435cb72672f9a81eb1a95c Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 15:57:12 +0700 Subject: [PATCH 53/81] detect img: remove index from file name --- script-opts/mpvi/{9_detect_image.yaml => detect_image.yaml} | 0 scripts/mpvi/{9_detect_image.lua => detect_image.lua} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename script-opts/mpvi/{9_detect_image.yaml => detect_image.yaml} (100%) rename scripts/mpvi/{9_detect_image.lua => detect_image.lua} (98%) diff --git a/script-opts/mpvi/9_detect_image.yaml b/script-opts/mpvi/detect_image.yaml similarity index 100% rename from script-opts/mpvi/9_detect_image.yaml rename to script-opts/mpvi/detect_image.yaml diff --git a/scripts/mpvi/9_detect_image.lua b/scripts/mpvi/detect_image.lua similarity index 98% rename from scripts/mpvi/9_detect_image.lua rename to scripts/mpvi/detect_image.lua index a0a5480..d9ddeb7 100644 --- a/scripts/mpvi/9_detect_image.lua +++ b/scripts/mpvi/detect_image.lua @@ -1,4 +1,4 @@ --- Detects when 🖼 are loaded and allows you to run commands configured in 1_detect_image.conf +-- Detects when 🖼 are loaded and allows you to run commands configured in detect_image.yaml local std = require "lib/std".std local parse_yaml = require "lib/tinyyaml".parse From 97198c979fd0eedff9af068a49ea0ab6e1e713a0 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 17:30:20 +0700 Subject: [PATCH 54/81] config: replace with YAML --- script-opts/mpvi/2_image_positioning.conf | 14 -------------- script-opts/mpvi/3_status_line.conf | 12 ------------ script-opts/mpvi/4_minimap.conf | 22 ---------------------- script-opts/mpvi/5_ruler.conf | 21 --------------------- script-opts/mpvi/image_positioning.yaml | 6 ++++++ script-opts/mpvi/minimap.yaml | 11 +++++++++++ script-opts/mpvi/ruler.yaml | 13 +++++++++++++ script-opts/mpvi/status_line.yaml | 11 +++++++++++ 8 files changed, 41 insertions(+), 69 deletions(-) delete mode 100644 script-opts/mpvi/2_image_positioning.conf delete mode 100644 script-opts/mpvi/3_status_line.conf delete mode 100644 script-opts/mpvi/4_minimap.conf delete mode 100644 script-opts/mpvi/5_ruler.conf create mode 100644 script-opts/mpvi/image_positioning.yaml create mode 100644 script-opts/mpvi/minimap.yaml create mode 100644 script-opts/mpvi/ruler.yaml create mode 100644 script-opts/mpvi/status_line.yaml diff --git a/script-opts/mpvi/2_image_positioning.conf b/script-opts/mpvi/2_image_positioning.conf deleted file mode 100644 index 7a47d71..0000000 --- a/script-opts/mpvi/2_image_positioning.conf +++ /dev/null @@ -1,14 +0,0 @@ -drag_to_pan_margin=50 -drag_to_pan_move_if_full_view=no -#↑ size of the margins with drag-to-pan - -pan_follows_cursor_margin=50 -#↑ size of the margins with pan-follows-cursor - -cursor_centric_zoom_margin=50 -#↑ size of the margins with cursor-centric-zoom - -cursor_centric_zoom_auto_center=yes -#↑ if the borders would show up, move the image this makes it not exactly cursor-centric in some cases -cursor_centric_zoom_dezoom_if_full_view=no -#↑ allow zooming out if the image can already fully fit in the window diff --git a/script-opts/mpvi/3_status_line.conf b/script-opts/mpvi/3_status_line.conf deleted file mode 100644 index 2434f03..0000000 --- a/script-opts/mpvi/3_status_line.conf +++ /dev/null @@ -1,12 +0,0 @@ -enabled=yes -#↑ whether to show by default -size=36 -#↑ its font size -margin=10 -#↑ distance of the text to the borders -text_top_left= -text_top_right= -text_bottom_left=${filename} [${playlist-pos-1}/${playlist-count}] -text_bottom_right=[${dwidth:X}x${dheight:X}] -# text to be expanded, see property expansion: https://mpv.io/manual/master/#property-expansion -# \N line breaks, can also use ass tags, see here: http://docs.aegisub.org/3.2/ASS_Tags/ diff --git a/script-opts/mpvi/4_minimap.conf b/script-opts/mpvi/4_minimap.conf deleted file mode 100644 index d766f49..0000000 --- a/script-opts/mpvi/4_minimap.conf +++ /dev/null @@ -1,22 +0,0 @@ -# boolean/numbers can't be converted with comments by github.com/mpv-player/mpv/blob/master/player/lua/options.lua -# other values also bug due to script imports not expecting comments -enabled=yes -#↑ whether to show by default -scale=12 -#↑ the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) -view_above_image=yes -#↑ whether the view should be drawn above the image -hide_when_full_image_in_view=yes -#↑ whether to show the minimap if the current image is fully visible -center=92,92 -#↑ the position of the center of the minimap, in percentage of the window (x, y) -max_size=16,16 -#↑ the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) -image_opacity=88 -#↑ opacity of the "image" (from 00=opaque to FF=transparent) -image_color=BBBBBB -#↑ color of the "image" (#BBGGRR where each component rages from 00 to FF) -view_opacity=BB -#↑ opacity of the "view" -view_color=222222 -#↑ diff --git a/script-opts/mpvi/5_ruler.conf b/script-opts/mpvi/5_ruler.conf deleted file mode 100644 index 66f8c9f..0000000 --- a/script-opts/mpvi/5_ruler.conf +++ /dev/null @@ -1,21 +0,0 @@ -show_distance=yes -#↑ whether to show the length of the lines between the two points -show_coordinates=yes -#↑ whether to show the coordinates of the two points -coordinates_space=image -#↑ the coordinate space of the text shown. Can be "image", "window", "both" -show_angles=degrees -#↑ can be "degrees", "radians", "both", or "no" -line_width=2 -dots_radius=3 -font_size=36 -line_color=33 -#↑ ranges from 00 (black) to FF (white) -confirm_bindings=MBTN_LEFT,ENTER -#↑ bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list -exit_bindings=ESC -#↑ bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list -set_first_point_on_begin=no -#↑ if yes, the first point will be immediately set at the cursor position when calling 'ruler' -clear_on_second_point_set=no -#↑ if yes, the ruler overlay will be immediately cleared when setting the second point diff --git a/script-opts/mpvi/image_positioning.yaml b/script-opts/mpvi/image_positioning.yaml new file mode 100644 index 0000000..7b71925 --- /dev/null +++ b/script-opts/mpvi/image_positioning.yaml @@ -0,0 +1,6 @@ +drag_to_pan_margin : 50 # +drag_to_pan_move_if_full_view : false # size of the margins with drag-to-pan +pan_follows_cursor_margin : 50 # size of the margins with pan-follows-cursor +cursor_centric_zoom_margin : 50 # size of the margins with cursor-centric-zoom +cursor_centric_zoom_auto_center : true # if the borders would show up, move the image this makes it not exactly cursor-centric in some cases +cursor_centric_zoom_dezoom_if_full_view : false # allow zooming out if the image can already fully fit in the window diff --git a/script-opts/mpvi/minimap.yaml b/script-opts/mpvi/minimap.yaml new file mode 100644 index 0000000..8474614 --- /dev/null +++ b/script-opts/mpvi/minimap.yaml @@ -0,0 +1,11 @@ +# ↓ "quote text" to avoid lua libyaml parser bug that includes #comments +enabled : true # whether to show by default +center : "92,92" # the position of the center of the minimap, in percentage of the window (x, y) +scale : 12 # the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) +max_size : "16,16" # the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) +image_opacity : "88" # opacity of the "image" (from 00=opaque to FF=transparent) +image_color : "BBBBBB" # color of the "image" (#BBGGRR where each component rages from 00 to FF) +view_opacity : "BB" # opacity of the "view" +view_color : "222222" # +view_above_image : true # whether the view should be drawn above the image +hide_when_full_image_in_view : true # whether to show the minimap if the current image is fully visible diff --git a/script-opts/mpvi/ruler.yaml b/script-opts/mpvi/ruler.yaml new file mode 100644 index 0000000..bb9b9bf --- /dev/null +++ b/script-opts/mpvi/ruler.yaml @@ -0,0 +1,13 @@ +# ↓ "quote text" to avoid lua libyaml parser bug that includes #comments +show_distance : true # show the length of the lines between the two points +show_coordinates : true # show the coordinates of the two points +coordinates_space : "image" # the coordinate space of the text shown. Can be "image", "window", "both" +show_angles : "degrees" # can be "degrees", "radians", "both", or "no" +line_width : 2 # +dots_radius : 3 # +font_size : 36 # +line_color : "00" # ranges from "00" (black) to ""FF"" (white) +confirm_bindings : "MBTN_LEFT,ENTER" # bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +exit_bindings : "ESC" # bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +set_first_point_on_begin : false # if yes, the first point will be immediately set at the cursor position when calling 'ruler' +clear_on_second_point_set : false # if yes, the ruler overlay will be immediately cleared when setting the second point diff --git a/script-opts/mpvi/status_line.yaml b/script-opts/mpvi/status_line.yaml new file mode 100644 index 0000000..6cb97f4 --- /dev/null +++ b/script-opts/mpvi/status_line.yaml @@ -0,0 +1,11 @@ +# ↓ "quote text" to avoid lua libyaml parser bug that includes #comments +enabled : true # whether to show by default +size : 36 # font size +margin : 10 # distance of the text to the borders +# ↓ text to be expanded # details @ mpv.io/manual/master +# line break \N # +# ass tags → # docs.aegisub.org/3.2/ASS_Tags/ +text_top_left : "" +text_top_right : "" +text_bottom_left : "${filename} [${playlist-pos-1}/${playlist-count}]" +text_bottom_right : "[${dwidth:X}x${dheight:X}]" From 6e5ed6de72314203435d14b3d62796b1aadc06e2 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 17:31:38 +0700 Subject: [PATCH 55/81] remove index from names and load YAML as config --- scripts/mpvi/detect_image.lua | 3 ++- .../{6_freeze_window.lua => freeze_window.lua} | 0 ...ge_positioning.lua => image_positioning.lua} | 4 ++-- scripts/mpvi/{4_minimap.lua => minimap.lua} | 5 +++-- scripts/mpvi/{5_ruler.lua => ruler.lua} | 17 ++++++----------- .../mpvi/{3_status_line.lua => status_line.lua} | 5 +++-- 6 files changed, 16 insertions(+), 18 deletions(-) rename scripts/mpvi/{6_freeze_window.lua => freeze_window.lua} (100%) rename scripts/mpvi/{2_image_positioning.lua => image_positioning.lua} (99%) rename scripts/mpvi/{4_minimap.lua => minimap.lua} (97%) rename scripts/mpvi/{5_ruler.lua => ruler.lua} (97%) rename scripts/mpvi/{3_status_line.lua => status_line.lua} (95%) diff --git a/scripts/mpvi/detect_image.lua b/scripts/mpvi/detect_image.lua index d9ddeb7..3274a44 100644 --- a/scripts/mpvi/detect_image.lua +++ b/scripts/mpvi/detect_image.lua @@ -1,4 +1,5 @@ --- Detects when 🖼 are loaded and allows you to run commands configured in detect_image.yaml +-- Detects when 🖼 are loaded and allows you to run commands from config +-- Configure via script-opts/mpvi/detect_image.yaml local std = require "lib/std".std local parse_yaml = require "lib/tinyyaml".parse diff --git a/scripts/mpvi/6_freeze_window.lua b/scripts/mpvi/freeze_window.lua similarity index 100% rename from scripts/mpvi/6_freeze_window.lua rename to scripts/mpvi/freeze_window.lua diff --git a/scripts/mpvi/2_image_positioning.lua b/scripts/mpvi/image_positioning.lua similarity index 99% rename from scripts/mpvi/2_image_positioning.lua rename to scripts/mpvi/image_positioning.lua index 972369a..5f5a85f 100644 --- a/scripts/mpvi/2_image_positioning.lua +++ b/scripts/mpvi/image_positioning.lua @@ -6,6 +6,7 @@ -- `pan-image` : pan the image in a direction, optionally ignoring the zoom or forcing the image to stay visible -- `rotate-video` : rotate the image in 90 degrees increment -- `reset-pan-if-visible` : reset the pan if the entire image is visible +-- Configure via script-opts/mpvi/image_positioning.yaml local std = require "lib/std".std @@ -32,8 +33,7 @@ local pan_max = 3.0 -- https://github.com/mpv-player/mpv/blob/67dbe2a8f469269 local align_min = -1.0 -- {"video-align-x", OPT_FLOAT(align_x), M_RANGE(-1.0, 1.0)}, local align_max = 1.0 -- https://github.com/mpv-player/mpv/blob/67dbe2a8f46926951af01a4ac91937f283898bb2/options/options.c#L143 -options.read_options(opts, opt_path_rel, function() end) --- msg.info("drag_to_pan_margin = " .. tostring(opts.drag_to_pan_margin)) +std.read_options_yaml(opts, opt_path_rel, function() end) local function clamp(value, low, high) if value <= low then return low diff --git a/scripts/mpvi/4_minimap.lua b/scripts/mpvi/minimap.lua similarity index 97% rename from scripts/mpvi/4_minimap.lua rename to scripts/mpvi/minimap.lua index 4f27469..65a7fa8 100644 --- a/scripts/mpvi/4_minimap.lua +++ b/scripts/mpvi/minimap.lua @@ -1,5 +1,6 @@ -- Adds a minimap that displays the position of the image relative to the view --- activated with `minimap-enable`, `minimap-disable`, `minimap-toggle` and configured via 4_minimap.conf +-- Activate with `minimap-enable`, `minimap-disable`, `minimap-toggle` +-- Configure via script-opts/mpvi/minimap.yaml local std = require "lib/std".std @@ -27,7 +28,7 @@ local msg = require 'mp.msg' local assdraw = require 'mp.assdraw' local options = require 'mp.options' -options.read_options(opts, opt_path_rel, function(c) +std.read_options_yaml(opts, opt_path_rel, function(c) if c["enabled"] then if opts.enabled then enable() else disable() end diff --git a/scripts/mpvi/5_ruler.lua b/scripts/mpvi/ruler.lua similarity index 97% rename from scripts/mpvi/5_ruler.lua rename to scripts/mpvi/ruler.lua index 6eeffbf..0bc3f2e 100644 --- a/scripts/mpvi/5_ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -1,5 +1,5 @@ -- Adds a `ruler` command that lets you measure positions, distances and angles in the image --- Configure via 5_ruler.conf +-- Configure via script-opts/mpvi/ruler.yaml local std = require "lib/std".std @@ -45,16 +45,11 @@ end local confirm_bindings = split(opts.confirm_bindings) local exit_bindings = split(opts.exit_bindings) -options.read_options(opts, opt_path_rel, function() - if state ~= 0 then - remove_bindings() - end - confirm_bindings = split(opts.confirm_bindings) - exit_bindings = split(opts.exit_bindings) - if state ~= 0 then - add_bindings() - mark_stale() - end +std.read_options_yaml(opts, opt_path_rel, function() + if state ~= 0 then remove_bindings() end + confirm_bindings = split(opts.confirm_bindings) + exit_bindings = split(opts.exit_bindings ) + if state ~= 0 then add_bindings(); mark_stale() end end) local ov = mp.create_osd_overlay("ass-events") diff --git a/scripts/mpvi/3_status_line.lua b/scripts/mpvi/status_line.lua similarity index 95% rename from scripts/mpvi/3_status_line.lua rename to scripts/mpvi/status_line.lua index 4170fb0..36e2ea8 100644 --- a/scripts/mpvi/3_status_line.lua +++ b/scripts/mpvi/status_line.lua @@ -1,5 +1,6 @@ -- Adds a status line that can show different properties in the corner of the window. By default it shows `filename [positon/total]` in the bottom left --- Can be activated with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` and configured through 3_status_line.conf +-- Activate with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` +-- Configure via script-opts/mpvi/status_line.yaml local std = require "lib/std".std @@ -24,7 +25,7 @@ local msg = require 'mp.msg' local assdraw = require 'mp.assdraw' local options = require 'mp.options' -options.read_options(opts, opt_path_rel, function(c) +std.read_options_yaml(opts, opt_path_rel, function(c) if c["enabled"] then if opts.enabled then enable() else disable() end end if c["size"] or c["margin"] then mark_stale() end From 547e12fd2b10b07a3951a9324cd860385fa46a65 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 17:32:28 +0700 Subject: [PATCH 56/81] std: convert cli args to numbers when needed --- scripts/mpvi/lib/std.lua | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 82337b7..031fdf8 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -103,6 +103,12 @@ function std.read_options_yaml(opts, identifier, on_update) if identifier == nil then identifier = mp.get_script_name() end msg.debug("reading options for "..identifier) + local function str_type_conv(val_cur, val_new) -- convert some strings too bool/number + if type(val_cur) == "number" then + if not (tonumber(val_new) == nil) then val_new = tonumber(val_new) end end + return val_new + end + -- get, read, and parse YAML config file local function read_yaml_config(identifier) -- get YAML config file contet local yaml_ext = {'yml','yaml'} @@ -146,8 +152,9 @@ function std.read_options_yaml(opts, identifier, on_update) if cfg_src then warn = warn.."\n in '"..cfg_src.."'" end msg.warn(warn) else - local type_def = type(cfg_def[k_def]) - local type_cfg = type(v) + local type_def = type(cfg_def[k_def]) + v = str_type_conv(cfg_def[k_def], v) + local type_cfg = type(v) if type_def ~= type_cfg then -- mismatch local warn = "[ignore] wrong key type: ".. "expected '"..type_def.. From 55e3bb24d25a76a126c75722ce74ce1614e0b8f6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Tue, 24 Jan 2023 17:33:05 +0700 Subject: [PATCH 57/81] main: remove index from names --- scripts/mpvi/main.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/scripts/mpvi/main.lua b/scripts/mpvi/main.lua index 2e649ea..af4cd27 100644 --- a/scripts/mpvi/main.lua +++ b/scripts/mpvi/main.lua @@ -8,9 +8,9 @@ function load(relative_path) end -- configs are @ script-opts/mpvi/.conf -load('2_image_positioning' ) -- add several high-level commands to zoom and pan -load('3_status_line' ) -- add a status line that can show different properties in the window corner -load('4_minimap' ) -- add a minimap that displays the position of the image relative to the view -load('5_ruler' ) -- add a `ruler` command that lets you measure positions, distances and angles in the image -load('6_freeze_window' ) -- disabled window auto resizing on file changes to fit its size -load('9_detect_image' ) -- detect when 🖼 are loaded, allows running commands from .conf +load('image_positioning' ) -- add several high-level commands to zoom and pan +load('status_line' ) -- add a status line that can show different properties in the window corner +load('minimap' ) -- add a minimap that displays the position of the image relative to the view +load('ruler' ) -- add a `ruler` command that lets you measure positions, distances and angles in the image +load('freeze_window' ) -- disabled window auto resizing on file changes to fit its size +load('detect_image' ) -- detect when 🖼 are loaded, allows running commands from .conf From 1c411a7dd198cad24866b6012c8d85886500631e Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 00:23:40 +0700 Subject: [PATCH 58/81] fix: stop() called before being declared --- scripts/mpvi/ruler.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/scripts/mpvi/ruler.lua b/scripts/mpvi/ruler.lua index 0bc3f2e..6259584 100644 --- a/scripts/mpvi/ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -268,6 +268,17 @@ local function remove_bindings() mp.remove_key_binding("ruler-mouse-move") end +local function stop() + if state == 0 then return end + mp.unregister_idle(refresh) + mp.unobserve_property(mark_stale) + remove_bindings() + state = 0 + first_point = nil + second_point = nil + hide_ov() +end + local function next_step() if state == 0 then state = 1 @@ -288,15 +299,4 @@ local function next_step() else stop() end end -local function stop() - if state == 0 then return end - mp.unregister_idle(refresh) - mp.unobserve_property(mark_stale) - remove_bindings() - state = 0 - first_point = nil - second_point = nil - hide_ov() -end - mp.add_key_binding(nil, "ruler", next_step) From d70e472851718383191a26b7cd766ee2fd08fb99 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:34:07 +0700 Subject: [PATCH 59/81] rule: fix local functions being invisible due to their order --- scripts/mpvi/ruler.lua | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/mpvi/ruler.lua b/scripts/mpvi/ruler.lua index 6259584..b032412 100644 --- a/scripts/mpvi/ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -257,18 +257,19 @@ local function mark_stale() video_dimensions_stale = true end -local function add_bindings() +local add_bindings, remove_bindings, stop, next_step +function add_bindings() mp.add_forced_key_binding("mouse_move", "ruler-mouse-move", mark_stale) for _, key in ipairs(confirm_bindings) do mp.add_forced_key_binding(key,"ruler-next-"..key,next_step) end for _, key in ipairs(exit_bindings) do mp.add_forced_key_binding(key,"ruler-stop-"..key,stop ) end end -local function remove_bindings() +function remove_bindings() for _, key in ipairs(confirm_bindings) do mp.remove_key_binding ( "ruler-next-"..key) end for _, key in ipairs(exit_bindings) do mp.remove_key_binding ( "ruler-stop-"..key) end mp.remove_key_binding("ruler-mouse-move") end -local function stop() +function stop() if state == 0 then return end mp.unregister_idle(refresh) mp.unobserve_property(mark_stale) @@ -279,7 +280,7 @@ local function stop() hide_ov() end -local function next_step() +function next_step() if state == 0 then state = 1 mp.register_idle(refresh) From d41652c1ec33afedde41d0d50c36620f00b71d5f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:34:38 +0700 Subject: [PATCH 60/81] std: add split and splitflex --- scripts/mpvi/lib/std.lua | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/scripts/mpvi/lib/std.lua b/scripts/mpvi/lib/std.lua index 031fdf8..8ed0861 100644 --- a/scripts/mpvi/lib/std.lua +++ b/scripts/mpvi/lib/std.lua @@ -5,6 +5,25 @@ string.escmagic = function(self, str) return self:gsub("([^%w])", "%%%1") string.startswith = function(self, str) return self:find('^' .. str:escmagic() ) ~= nil end string.endswith = function(self, str) return self:find( str:escmagic() .. '$') ~= nil end string.trim = function(self ) return self:match("^%s*(.-)%s*$") end +string.split = function(self, sep) + if sep == nil then sep = ',' end + local ret = {} + for str in string.gmatch(self, "([^"..sep.."]+)") do + ret[#ret + 1] = str end + return ret +end +string.splitflex = function(self, sep) + local seps = {',',';'} + if sep == nil then + for k,v in pairs(seps) do + if self:find(v) then sep = v end end + if sep == nil then sep = ' ' end -- finally, use space + end + local ret = {} + for str in string.gmatch(self, "([^"..sep.."]+)") do + ret[#ret + 1] = str end + return ret +end function std.getScriptFullPath() local source = debug.getinfo(2,"S").source From 7a71b0e3bd39ecb28a49448e7f5a57d93451c587 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:35:23 +0700 Subject: [PATCH 61/81] lib: add color conversion functions --- scripts/mpvi/lib/color.lua | 387 +++++++++++++++++++++++++++++++++++++ 1 file changed, 387 insertions(+) create mode 100644 scripts/mpvi/lib/color.lua diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua new file mode 100644 index 0000000..d648b9f --- /dev/null +++ b/scripts/mpvi/lib/color.lua @@ -0,0 +1,387 @@ +local std = require "lib/std".std + +local color = {} -- color helper functions + +-- functions adapted from +-- https://github.com/EmmanuelOga/columns/blob/master/utils/color.lua +-- https://github.com/bottosson/bottosson.github.io/blob/master/misc/colorpicker/colorconversion.js +local function toe_inv(x) + local k_1 = 0.206 + local k_2 = 0.03 + local k_3 = (1+k_1)/(1+k_2) + return (x*x + k_1*x)/(k_3*(x+k_2)) +end +local function srgb_transfer_function(a) + if .0031308 >= a then return 12.92 * a + else return 1.055 * a^(.4166666666666667) - .055 end +end +local function oklab2linear_srgb(L,a,b) + local l_ = L + 0.3963377774 * a + 0.2158037573 * b + local m_ = L - 0.1055613458 * a - 0.0638541728 * b + local s_ = L - 0.0894841775 * a - 1.2914855480 * b + + local l = l_*l_*l_ + local m = m_*m_*m_ + local s = s_*s_*s_ + + return ( 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s), + (-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s), + (-0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s) +end +local function compute_max_saturation(a, b) -- Finds the maximum saturation possible for a given hue that fits in sRGB Saturation here is defined as S = C/L a and b must be normalized so a^2 + b^2 == 1 + -- Max saturation will be when one of r, g or b goes below zero + -- Select different coefficients depending on which component goes below zero first + local k0, k1, k2, k3, k4, wl, wm, ws + + if (-1.88170328 * a - 0.80936493 * b > 1) then -- Red component + k0 = 1.19086277 ; k1 = 1.76576728 ; k2 = 0.59662641 ; k3 = 0.75515197; k4 = 0.56771245 + wl = 4.0767416621 ; wm = -3.3077115913; ws = 0.2309699292 + elseif ( 1.81444104 * a - 1.19445276 * b > 1) then -- Green component + k0 = 0.73956515 ; k1 = -0.45954404 ; k2 = 0.08285427 ; k3 = 0.12541070; k4 = 0.14503204 + wl = -1.2684380046; wm = 2.6097574011; ws = -0.3413193965 + else -- Blue component + k0 = 1.35733652 ; k1 = -0.00915799 ; k2 = -1.15130210 ; k3 = -0.50559606; k4 = 0.00692167 + wl = -0.0041960863; wm = -0.7034186147; ws = 1.7076147010 + end + + local S = k0 + k1 * a + k2 * b + k3 * a * a + k4 * a * b -- Approximate max saturation using a polynomial + + -- 1 step Halley's method to get closer; error < 10e6, except for some blue hues where the dS/dh ≈ ∞ + -- should be sufficient for most applications, otherwise do two/three steps + local k_l = 0.3963377774 * a + 0.2158037573 * b + local k_m = -0.1055613458 * a - 0.0638541728 * b + local k_s = -0.0894841775 * a - 1.2914855480 * b + + local l_ = 1 + S * k_l + local m_ = 1 + S * k_m + local s_ = 1 + S * k_s + -- -- + local l = l_ * l_ * l_ + local m = m_ * m_ * m_ + local s = s_ * s_ * s_ + -- -- + local l_dS = 3 * k_l * l_ * l_ + local m_dS = 3 * k_m * m_ * m_ + local s_dS = 3 * k_s * s_ * s_ + -- -- + local l_dS2 = 6 * k_l * k_l * l_ + local m_dS2 = 6 * k_m * k_m * m_ + local s_dS2 = 6 * k_s * k_s * s_ + -- -- + local f = wl * l + wm * m + ws * s + local f1 = wl * l_dS + wm * m_dS + ws * s_dS + local f2 = wl * l_dS2 + wm * m_dS2 + ws * s_dS2 + + S = S - f * f1 / (f1*f1 - 0.5 * f * f2) + + return S +end +local function find_cusp(a, b) + local S_cusp = compute_max_saturation(a, b) -- find the maximum saturation (saturation S = C/L) + + -- Convert to linear sRGB to find the first point where at least one of r,g or b >= 1 + local r, g, b = oklab2linear_srgb(1, S_cusp * a, S_cusp * b) -- rgb@max + local L_cusp = (1 / math.max(math.max(r,g), b))^(1/3) + local C_cusp = L_cusp * S_cusp + + return {L_cusp , C_cusp} +end +function find_gamut_intersection(a, b, L1, C1, L0, cusp) -- Finds intersection of the line defined by + -- L = L0 * (1 - t) + t * L1; + -- C = t * C1; + -- a and b must be normalized so a^2 + b^2 == 1 + if not cusp then cusp = find_cusp(a,b) end -- Find the cusp of the gamut triangle + + -- Find the intersection for upper and lower half seprately + local t + if ((L1 - L0) * cusp[2] - (cusp[1] - L0) * C1) <= 0 then -- Lower half + t = cusp[2] * L0 / (C1 * cusp[1] + cusp[2] * (L0 - L1)); + else -- Upper half + -- First intersect with triangle + t = cusp[2] * (L0 - 1) / (C1 * (cusp[1] - 1) + cusp[2] * (L0 - L1)); + + -- Then one step Halley's method + local dL = L1 - L0; + local dC = C1; + -- -- + local k_l = 0.3963377774 * a + 0.2158037573 * b; + local k_m = -0.1055613458 * a - 0.0638541728 * b; + local k_s = -0.0894841775 * a - 1.2914855480 * b; + -- -- + local l_dt = dL + dC * k_l; + local m_dt = dL + dC * k_m; + local s_dt = dL + dC * k_s; + + -- For higher accuracy do 2 or 3 iterations of the following + local L = L0 * (1 - t) + t * L1; + local C = t * C1; + -- -- + local l_ = L + C * k_l; + local m_ = L + C * k_m; + local s_ = L + C * k_s; + -- -- + local l = l_ * l_ * l_; + local m = m_ * m_ * m_; + local s = s_ * s_ * s_; + -- -- + local ldt = 3 * l_dt * l_ * l_; + local mdt = 3 * m_dt * m_ * m_; + local sdt = 3 * s_dt * s_ * s_; + -- -- + local ldt2 = 6 * l_dt * l_dt * l_; + local mdt2 = 6 * m_dt * m_dt * m_; + local sdt2 = 6 * s_dt * s_dt * s_; + -- -- + local r = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s - 1; + local r1 = 4.0767416621 * ldt - 3.3077115913 * mdt + 0.2309699292 * sdt; + local r2 = 4.0767416621 * ldt2 - 3.3077115913 * mdt2 + 0.2309699292 * sdt2; + -- -- + local u_r = r1 / (r1 * r1 - 0.5 * r * r2); + local t_r = -r * u_r; + -- -- + local g = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s - 1; + local g1 = -1.2684380046 * ldt + 2.6097574011 * mdt - 0.3413193965 * sdt; + local g2 = -1.2684380046 * ldt2 + 2.6097574011 * mdt2 - 0.3413193965 * sdt2; + -- -- + local u_g = g1 / (g1 * g1 - 0.5 * g * g2); + local t_g = -g * u_g; + -- -- + local b = -0.0041960863 * l - 0.7034186147 * m + 1.7076147010 * s - 1; + local b1 = -0.0041960863 * ldt - 0.7034186147 * mdt + 1.7076147010 * sdt; + local b2 = -0.0041960863 * ldt2 - 0.7034186147 * mdt2 + 1.7076147010 * sdt2; + -- -- + local u_b = b1 / (b1 * b1 - 0.5 * b * b2); + local t_b = -b * u_b; + + if u_r < 0 then t_r = 10e5 end + if u_g < 0 then t_g = 10e5 end + if u_b < 0 then t_b = 10e5 end + + t = t + math.min(t_r, math.min(t_g,t_b)) + end + + return t +end + +function get_ST_max(a_,b_, cusp) + if not cusp then cusp = find_cusp(a_, b_) end + + local L = cusp[1] + local C = cusp[2] + return {C/L, C/(1-L)} +end + +local function get_ST_mid(a_,b_) + local S = 0.11516993 + 1/( + 0+ 7.44778970 + 4.15901240*b_ + + a_*(-2.19557347 + 1.75198401*b_ + + a_*(-2.13704948 -10.02301043*b_ + + a_*(-4.24894561 + 5.38770819*b_ + 4.69891013*a_ ))) + ) + + local T = 0.11239642 + 1/( + 0+ 1.61320320 - 0.68124379*b_ + + a_*( 0.40370612 + 0.90148123*b_ + + a_*(-0.27087943 + 0.61223990*b_ + + a_*( 0.00299215 - 0.45399568*b_ - 0.14661872*a_ ))) + ) + + return {S, T} +end +local function get_Cs(L, a_, b_) + local cusp = find_cusp(a_, b_) + local C_max = find_gamut_intersection(a_,b_,L,1,L,cusp) + local ST_max = get_ST_max(a_, b_, cusp) + + local S_mid = 0.11516993 + 1/(0 + + 7.44778970 + 4.15901240*b_ + + a_*(-2.19557347 + 1.75198401*b_ + + a_*(-2.13704948 -10.02301043*b_ + + a_*(-4.24894561 + 5.38770819*b_ + 4.69891013*a_ ))) + ) + + local T_mid = 0.11239642 + 1/(0 + + 1.61320320 - 0.68124379*b_ + + a_*( 0.40370612 + 0.90148123*b_ + + a_*(-0.27087943 + 0.61223990*b_ + + a_*( 0.00299215 - 0.45399568*b_ - 0.14661872*a_ ))) + ) + + local k = C_max/math.min((L*ST_max[1]), (1-L)*ST_max[2]) + + local C_mid + local C_a = L*S_mid + local C_b = (1-L)*T_mid + C_mid = 0.9*k*math.sqrt(math.sqrt(1/(1/(C_a*C_a*C_a*C_a) + 1/(C_b*C_b*C_b*C_b)))) + + local C_0 + local C_a = L*0.4 + local C_b = (1-L)*0.8 + C_0 = math.sqrt(1/(1/(C_a*C_a) + 1/(C_b*C_b))) + + return {C_0, C_mid, C_max} +end + +function color.hsl2rgb(h, s, l) + if type(h) == 'string' then h = tonumber(h) end -- normalize str→num + if type(s) == 'string' then s = tonumber(s) end + if type(l) == 'string' then l = tonumber(l) end + if h > 1 then h = h / 360 end -- normalize + if s > 1 then s = s / 100 end + if l > 1 then l = l / 100 end + local r,g,b + + if (s == 0) then r,g,b = l,l,l -- achromatic + else + local function hue2rgb(p, q, t) + if (t < 0 ) then t = t + 1 + elseif (t > 1 ) then t = t - 1 end + if (t < 1/6) then return p + (q - p) * t * 6 + elseif (t < 3/6) then return q + elseif (t < 4/6) then return p + (q - p) * (2/3 - t) * 6 + else return p end + end + + local q + if (l < 1/2) then q = l * (1 + s) + else q = l * (1 - s) + s end + local p = 2 * l - q + r = hue2rgb(p, q, h + 1/3) + g = hue2rgb(p, q, h ) + b = hue2rgb(p, q, h - 1/3) + end + return -- min to avoid returning values > 255; also round + std.round(255*math.min(r,1)), + std.round(255*math.min(g,1)), + std.round(255*math.min(b,1)) +end + +function color.okhsl2srgb(h,s,l) + if type(h) == 'string' then h = tonumber(h) end -- normalize str→num + if type(s) == 'string' then s = tonumber(s) end + if type(l) == 'string' then l = tonumber(l) end + if h > 1 then h = h / 360 end -- normalize + if s > 1 then s = s / 100 end + if l > 1 then l = l / 100 end + local r,g,b + + if (l == 1) then return 255,255,255 + elseif (l == 0) then return 0, 0, 0 end + + local a_ = math.cos(2*math.pi * h) + local b_ = math.sin(2*math.pi * h) + local L = toe_inv(l) + + local Cs = get_Cs(L, a_, b_) + local C_0 = Cs[1] + local C_mid = Cs[2] + local C_max = Cs[3] + + local C, t, k_0, k_1, k_2 + if (s < 0.8) then + t = 1.25*s + k_0 = 0 + k_1 = 0.8*C_0 + k_2 = (1 - k_1/C_mid) + else + t = 5*(s-0.8) + k_0 = C_mid + k_1 = 0.2*C_mid*C_mid*1.25*1.25/C_0 + k_2 = (1 - k_1/(C_max - C_mid)) + end + + C = k_0 + t*k_1/(1-k_2*t) + + -- If we would only use one of the Cs: + -- C = s*C_0 + -- C = s*1.25*C_mid + -- C = s*C_max + + r,g,b = oklab2linear_srgb(L, C*a_, C*b_) + return -- min to avoid returning values > 255; also round + std.round(255*math.min(srgb_transfer_function(r),1)), + std.round(255*math.min(srgb_transfer_function(g),1)), + std.round(255*math.min(srgb_transfer_function(b),1)) +end + + +function color.hex2rgb(hex) -- also converts short rgbs + -- https://gist.github.com/fernandohenriques/12661bf250c8c2d8047188222cab7e28 + local hex = hex:gsub("#","") + local len = hex:len() + if len == 2 then return tonumber("0x"..hex:sub(1,2)) /255, -- short #CC + tonumber("0x"..hex:sub(1,2)) /255, + tonumber("0x"..hex:sub(1,2)) /255 + elseif len == 3 then return (tonumber("0x"..hex:sub(1,1))*17)/255, -- short #RGB + (tonumber("0x"..hex:sub(2,2))*17)/255, + (tonumber("0x"..hex:sub(3,3))*17)/255 + elseif len == 6 then return tonumber("0x"..hex:sub(1,2)) /255, -- regular #RRGGBB + tonumber("0x"..hex:sub(3,4)) /255, + tonumber("0x"..hex:sub(5,6)) /255 + else print("wrong input length ("..len.."), should be 2, 3, or 6 symbols without #: "..hex) end +end +function color.hex2a(hex) + local hex = hex:gsub("#","") + local len = hex:len() + if len == 2 then return tonumber("0x"..hex:sub(1,2)) /255 -- short #AA + else print("wrong input length ("..len.."), should be 2 symbols without #: "..hex) end +end +function color.hex2rev(hex) + local hex = hex:gsub("#","") + local len = hex:len() + local hexbgr + if len == 2 then hexbgr = hex:sub(1,2)..hex:sub(1,2)..hex:sub(1,2) + elseif len == 3 then hexbgr = hex:sub(3,3)..hex:sub(2,2)..hex:sub(1,1) + elseif len == 6 then hexbgr = hex:sub(5,6)..hex:sub(3,4)..hex:sub(1,2) + else print("wrong input length ("..len.."), should be 2, 3, or 6 symbols without #: "..hex) end + return hexbgr, "#"..tostring(hexbgr) +end +function color.rgb2hex(r,g,b) + if type(r) == 'string' then r = tonumber(r) end -- normalize str→num + if type(g) == 'string' then g = tonumber(g) end + if type(b) == 'string' then b = tonumber(b) end + if r>255 or g>255 or b>255 then print("error: r,g,b > 255"); return end + if r< 0 or g< 0 or b< 0 then print("error: r,g,b < 0" ); return end + local hex = string.format("%02X%02X%02X",r,g,b) + return hex , "#"..tostring(hex) +end +function color.rgb2hexbgr(r,g,b) + if type(r) == 'string' then r = tonumber(r) end -- normalize str→num + if type(g) == 'string' then g = tonumber(g) end + if type(b) == 'string' then b = tonumber(b) end + if r>255 or g>255 or b>255 then print("error: r,g,b > 255"); return end + if r< 0 or g< 0 or b< 0 then print("error: r,g,b < 0" ); return end + return color.rgb2hex(b,g,r) +end +function color.a2hex(a) + if type(a) == 'string' then a = tonumber(a) end -- normalize str→num + if a>255 then print("error: a > 255"); return end + if a< 0 then print("error: a < 0" ); return end + local hex = string.format("%02X",a) + return hex , "#"..tostring(hex) +end + +function color.convert2mpv(color_space, col_in, sep) + if sep == nil then sep = ' ' end + if col_in:startswith("#") then + if not color_space == "hex" then + print("color type mismatch: passed hex color ("..col_in.."), but non-hex color space ("..color_space..")") + color_space = "hex" end end + local col_conv + if color_space:lower() == "okhsl" then + local hsl = col_in:splitflex(sep) + local r,g,b = color.okhsl2srgb(hsl[1],hsl[2],hsl[3]) + col_conv = color.rgb2hexbgr(r,g,b) + elseif color_space:lower() == "hsl" then + local hsl = col_in:splitflex(sep) + local r,g,b = color.hsl2rgb(hsl[1],hsl[2],hsl[3]) + col_conv = color.rgb2hexbgr(r,g,b) + elseif color_space:lower() == "hex" then + col_conv = color.hex2rev(col_in) + end + return col_conv +end + +return { + color = color, +} From 3b2e2985cb5d32dfabe020a90d8400149a8e6fd9 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:36:15 +0700 Subject: [PATCH 62/81] ruler: change to better color space --- scripts/mpvi/ruler.lua | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/mpvi/ruler.lua b/scripts/mpvi/ruler.lua index b032412..b3bb858 100644 --- a/scripts/mpvi/ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -1,7 +1,8 @@ -- Adds a `ruler` command that lets you measure positions, distances and angles in the image -- Configure via script-opts/mpvi/ruler.yaml -local std = require "lib/std".std +local std = require "lib/std".std +local color = require "lib/color".color local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi local script_dir_base = std.basename(script_dir) -- mpvi @@ -18,7 +19,8 @@ local opts = { line_width = 2, dots_radius = 3, font_size = 36, - line_color = "33", + color_space = "okhsl", + line_color = "57 100 70", confirm_bindings = "MBTN_LEFT,ENTER", exit_bindings = "ESC", set_first_point_on_begin = false, @@ -52,6 +54,9 @@ std.read_options_yaml(opts, opt_path_rel, function() if state ~= 0 then add_bindings(); mark_stale() end end) +local line_color = color.convert2mpv(opts.color_space, opts.line_color) +if line_color == nil then msg.error("ruler: wrong config line color "..opts.line_color); line_color = "000000" end + local ov = mp.create_osd_overlay("ass-events") local function hide_ov() @@ -128,8 +133,7 @@ local function refresh() a:pos(0,0) a:append("{\\bord" .. bord .. "}") a:append("{\\shad0}") - local r = opts.line_color - a:append("{\\3c&H".. r .. r .. r .. "&}") + a:append("{\\3c&H"..line_color.."&}") a:append("{\\1a&HFF}") a:append("{\\2a&HFF}") a:append("{\\3a&H00}") From f337ea4df4001a483bfcc6335959c1c3c4435851 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:38:18 +0700 Subject: [PATCH 63/81] minimap: change to better color space --- scripts/mpvi/minimap.lua | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/scripts/mpvi/minimap.lua b/scripts/mpvi/minimap.lua index 65a7fa8..c166cfd 100644 --- a/scripts/mpvi/minimap.lua +++ b/scripts/mpvi/minimap.lua @@ -2,7 +2,8 @@ -- Activate with `minimap-enable`, `minimap-disable`, `minimap-toggle` -- Configure via script-opts/mpvi/minimap.yaml -local std = require "lib/std".std +local std = require "lib/std".std +local color = require "lib/color".color local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi local script_dir_base = std.basename(script_dir) -- mpvi @@ -16,10 +17,11 @@ local opts = { center = "92,92", scale = 12, max_size = "16,16", - image_opacity = "88", - image_color = "BBBBBB", - view_opacity = "BB", - view_color = "222222", + color_space = "okhsl", + image_opacity = "50", + image_color = "0 0 75", + view_opacity = "75", + view_color = "0 0 15", view_above_image = true, hide_when_full_image_in_view = true, } @@ -43,6 +45,14 @@ local function split_comma(input) end return ret end +local image_color = color.convert2mpv(opts.color_space, opts.image_color ) +local view_color = color.convert2mpv(opts.color_space, opts.view_color ) +local image_opacity = color.a2hex( opts.image_opacity) +local view_opacity = color.a2hex( opts.view_opacity ) +if image_color == nil then msg.error("ruler: wrong config image color " ..opts.image_color); image_color = "BBBBBB" end +if view_color == nil then msg.error("ruler: wrong config view color " ..opts.view_color) ; view_color = "222222" end +if image_opacity == nil then msg.error("ruler: wrong config image opacity " ..opts.view_color) ; image_opacity = "88" end +if view_opacity == nil then msg.error("ruler: wrong config view opacity " ..opts.view_color) ; view_opacity = "BB" end local active = false local refresh = true @@ -137,11 +147,11 @@ local function refresh_ui() local draw_image = function() draw( ( dim.ml/2 - dim.mr/2) / opts.scale, ( dim.mt/2 - dim.mb/2) / opts.scale, (ww - dim.ml - dim.mr ) / opts.scale, (wh - dim.mt - dim.mb ) / opts.scale, - opts.image_opacity, opts.image_color) end + image_opacity, image_color) end local draw_view = function() draw( 0 , 0, ww / opts.scale, wh / opts.scale, - opts.view_opacity , opts.view_color ) end + view_opacity , view_color ) end if opts.view_above_image then draw_image(); draw_view () else draw_view (); draw_image() end draw_ov(a.text) From 94b5526adf7ed2160218839e482b3e71eed8ad2a Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:38:33 +0700 Subject: [PATCH 64/81] minimap: change to flexible splits --- scripts/mpvi/minimap.lua | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/scripts/mpvi/minimap.lua b/scripts/mpvi/minimap.lua index c166cfd..a13b2f0 100644 --- a/scripts/mpvi/minimap.lua +++ b/scripts/mpvi/minimap.lua @@ -14,9 +14,9 @@ local opt_path_rel = script_dir_base ..'/'.. script_stem -- mpvi/= 0 and dim.mr >= 0 ) then hide_ov(); return end end - local center = split_comma(opts.center) + local center = string.splitflex(opts.center) center[1] = center[1] * 0.01 * ww center[2] = center[2] * 0.01 * wh - local cutoff = split_comma(opts.max_size) + local cutoff = string.splitflex(opts.max_size) cutoff[1] = cutoff[1] * 0.01 * ww * 0.5 cutoff[2] = cutoff[2] * 0.01 * wh * 0.5 From fb422279056529292f9ce5550e18465ed96d39ca Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:39:05 +0700 Subject: [PATCH 65/81] ruler: change to flexible splits --- scripts/mpvi/ruler.lua | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/scripts/mpvi/ruler.lua b/scripts/mpvi/ruler.lua index b3bb858..67677ed 100644 --- a/scripts/mpvi/ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -36,21 +36,13 @@ local first_point = nil -- in normalized video space coordinates local second_point = nil -- in normalized video space coordinates local video_dimensions_stale = false -local function split(input) - local ret = {} - for str in string.gmatch(input, "([^,]+)") do - ret[#ret + 1] = str - end - return ret -end - -local confirm_bindings = split(opts.confirm_bindings) -local exit_bindings = split(opts.exit_bindings) +local confirm_bindings = string.splitflex(opts.confirm_bindings) +local exit_bindings = string.splitflex(opts.exit_bindings) std.read_options_yaml(opts, opt_path_rel, function() if state ~= 0 then remove_bindings() end - confirm_bindings = split(opts.confirm_bindings) - exit_bindings = split(opts.exit_bindings ) + confirm_bindings = string.splitflex(opts.confirm_bindings) + exit_bindings = string.splitflex(opts.exit_bindings ) if state ~= 0 then add_bindings(); mark_stale() end end) From 14dc67b3db395e68ad3ec48ac7660cad4ace3231 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:39:37 +0700 Subject: [PATCH 66/81] minimap: update config with flexible splits and better color space --- script-opts/mpvi/minimap.yaml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/script-opts/mpvi/minimap.yaml b/script-opts/mpvi/minimap.yaml index 8474614..97df826 100644 --- a/script-opts/mpvi/minimap.yaml +++ b/script-opts/mpvi/minimap.yaml @@ -1,11 +1,12 @@ # ↓ "quote text" to avoid lua libyaml parser bug that includes #comments enabled : true # whether to show by default -center : "92,92" # the position of the center of the minimap, in percentage of the window (x, y) +center : "92 92" # the position of the center of the minimap, in percentage of the window (x, y) scale : 12 # the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) -max_size : "16,16" # the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) -image_opacity : "88" # opacity of the "image" (from 00=opaque to FF=transparent) -image_color : "BBBBBB" # color of the "image" (#BBGGRR where each component rages from 00 to FF) -view_opacity : "BB" # opacity of the "view" -view_color : "222222" # +max_size : "16 16" # the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) +color_space : "okhsl" # "okhsl", "hsl", or "hex" bottosson.github.io/misc/colorpicker +image_opacity : "50" # opacity of the "image" (from 0=opaque to 100=transparent) +image_color : "0 0 75" # color of the "image" +view_opacity : "100" # opacity of the "view" +view_color : "0 0 15" # color of the "view" view_above_image : true # whether the view should be drawn above the image hide_when_full_image_in_view : true # whether to show the minimap if the current image is fully visible From 80fb2c0397a9f7ba76911bc64d57cafd72d0aa2f Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Wed, 25 Jan 2023 02:39:45 +0700 Subject: [PATCH 67/81] ruler: update config with flexible splits and better color space --- script-opts/mpvi/ruler.yaml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/script-opts/mpvi/ruler.yaml b/script-opts/mpvi/ruler.yaml index bb9b9bf..fd2442d 100644 --- a/script-opts/mpvi/ruler.yaml +++ b/script-opts/mpvi/ruler.yaml @@ -3,11 +3,12 @@ show_distance : true # show the length of the lines bet show_coordinates : true # show the coordinates of the two points coordinates_space : "image" # the coordinate space of the text shown. Can be "image", "window", "both" show_angles : "degrees" # can be "degrees", "radians", "both", or "no" -line_width : 2 # -dots_radius : 3 # +line_width : 2 # +dots_radius : 3 # font_size : 36 # -line_color : "00" # ranges from "00" (black) to ""FF"" (white) -confirm_bindings : "MBTN_LEFT,ENTER" # bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list -exit_bindings : "ESC" # bindings used to set points. The binding to trigger ruler mode can also be used. Comma-separated list +color_space : "okhsl" # "okhsl", "hsl", or "hex" bottosson.github.io/misc/colorpicker +line_color : "57 100 70" # +confirm_bindings : "MBTN_LEFT ENTER" # bindings used to set points. The binding to trigger ruler mode can also be used. Space/comma-separated list +exit_bindings : "ESC" # bindings used to set points. The binding to trigger ruler mode can also be used. Space/comma-separated list set_first_point_on_begin : false # if yes, the first point will be immediately set at the cursor position when calling 'ruler' clear_on_second_point_set : false # if yes, the ruler overlay will be immediately cleared when setting the second point From 6a57f855d800c4958c1c3c38cacfe346f46f34ce Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 26 Jan 2023 04:27:27 +0700 Subject: [PATCH 68/81] lib: color: add various normalization and conversion helper functions --- scripts/mpvi/lib/color.lua | 116 ++++++++++++++++++++++++++++--------- 1 file changed, 89 insertions(+), 27 deletions(-) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index d648b9f..3f78a66 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -222,13 +222,30 @@ local function get_Cs(L, a_, b_) return {C_0, C_mid, C_max} end +function color.hsl2norm(h,s,l,slmax) -- checks input for errors and returns 1-based numbers + local norm_str = 0 -- normalize str→num + if type(h) == 'string' then h = tonumber(h); norm_str = norm_str + 1 end + if type(s) == 'string' then s = tonumber(s); norm_str = norm_str + 1 end + if type(l) == 'string' then l = tonumber(l); norm_str = norm_str + 1 end + if 0 < norm_str and norm_str < 3 then print("warning: some, but not all, input was of type 'string'") end + + if h < 0 or s < 0 or l < 0 then print("error: h|s|l < 0 ("..h..","..s..","..l..")"); return end + if slmax == nil then + if h > 1 or s > 1 or l > 1 then -- h/s/l=1 assumed to be 0–1, not 0–360 or 0–100 + slmax = 100 + else slmax = 1 end end + if not slmax == 1 and not slmax == 100 then print("error: max scale should be 1 or 100, not"..tostring(slmax)); return end + if slmax == 1 then + if h > slmax or s > slmax or l > slmax then print("error: h|s|l above max="..slmax.." ("..h..","..s..","..l..")"); return end end + if slmax == 100 then -- normalize 360 or 100 → 1 + if h > 360 or s > slmax or l > slmax then print("error: h|s|l above max=360 or"..slmax.." ("..h..","..s..","..l..")"); return end + h = h/360; s = s/100; l = l/100 + end + + return h,s,l +end function color.hsl2rgb(h, s, l) - if type(h) == 'string' then h = tonumber(h) end -- normalize str→num - if type(s) == 'string' then s = tonumber(s) end - if type(l) == 'string' then l = tonumber(l) end - if h > 1 then h = h / 360 end -- normalize - if s > 1 then s = s / 100 end - if l > 1 then l = l / 100 end + h,s,l = color.hsl2norm(h,s,l) local r,g,b if (s == 0) then r,g,b = l,l,l -- achromatic @@ -257,12 +274,7 @@ function color.hsl2rgb(h, s, l) end function color.okhsl2srgb(h,s,l) - if type(h) == 'string' then h = tonumber(h) end -- normalize str→num - if type(s) == 'string' then s = tonumber(s) end - if type(l) == 'string' then l = tonumber(l) end - if h > 1 then h = h / 360 end -- normalize - if s > 1 then s = s / 100 end - if l > 1 then l = l / 100 end + h,s,l = color.hsl2norm(h,s,l) local r,g,b if (l == 1) then return 255,255,255 @@ -336,28 +348,58 @@ function color.hex2rev(hex) else print("wrong input length ("..len.."), should be 2, 3, or 6 symbols without #: "..hex) end return hexbgr, "#"..tostring(hexbgr) end +function color.rgb2norm(r,b,g,max) -- checks input for errors and returns 255-based numbers + local norm_str = 0 -- normalize str→num + if type(r) == 'string' then r = tonumber(r); norm_str = norm_str + 1 end + if type(g) == 'string' then g = tonumber(g); norm_str = norm_str + 1 end + if type(b) == 'string' then b = tonumber(b); norm_str = norm_str + 1 end + if 0 < norm_str and norm_str < 3 then print("warning: some, but not all, input was of type 'string'") end + + if r < 0 or g < 0 or b < 0 then print("error: r|g|b < 0" ); return end + if max == nil then + if r > 1 or g > 1 or b > 1 then -- r/g/b=1 assumed to be 0–1, not 0–255 + max = 255 + else max = 1 end end + if not max == 1 and not max == 255 then print("error: max scale should be 1 or 255, not"..tostring(max)); return end + if r > max or g > max or b > max then print("error: r|g|b > max="..max.." ("..r..","..g..","..b..")"); return end + if max == 1 then -- normalize 1 → 255 + r = 255*r; g = 255*g; b = 255*b end + + return r,g,b +end function color.rgb2hex(r,g,b) - if type(r) == 'string' then r = tonumber(r) end -- normalize str→num - if type(g) == 'string' then g = tonumber(g) end - if type(b) == 'string' then b = tonumber(b) end - if r>255 or g>255 or b>255 then print("error: r,g,b > 255"); return end - if r< 0 or g< 0 or b< 0 then print("error: r,g,b < 0" ); return end + r,g,b = color.rgb2norm(r,g,b) local hex = string.format("%02X%02X%02X",r,g,b) return hex , "#"..tostring(hex) end function color.rgb2hexbgr(r,g,b) - if type(r) == 'string' then r = tonumber(r) end -- normalize str→num - if type(g) == 'string' then g = tonumber(g) end - if type(b) == 'string' then b = tonumber(b) end - if r>255 or g>255 or b>255 then print("error: r,g,b > 255"); return end - if r< 0 or g< 0 or b< 0 then print("error: r,g,b < 0" ); return end + r,g,b = color.rgb2norm(r,g,b) return color.rgb2hex(b,g,r) end -function color.a2hex(a) - if type(a) == 'string' then a = tonumber(a) end -- normalize str→num - if a>255 then print("error: a > 255"); return end - if a< 0 then print("error: a < 0" ); return end - local hex = string.format("%02X",a) +function color.a2norm(a,max) -- checks alpha input for errors and returns 1-based alpha + if type(a) == 'string' then a = tonumber(a) end -- normalize str→num + + if a < 0 then print("error: a < 0 (" ..a..")"); return end + if max == nil then + if a > 100 then max = 255 + elseif a > 1 then max = 100 -- a=1 assumed to be 0–1, not 0–100 or 0–255 + else max = 1 end end + if not max == 1 and not max == 100 and not max == 255 then print("error: max scale should be 1, 100, or 255, not"..tostring(max)); return end + if a > max then print("error: a > max="..max.." ("..a..")"); return end + + if max == 255 then a = a/255 -- normalize 100 → 1 + elseif max == 100 then a = a/100 end -- normalize 255 → 1 + + return a +end +function color.a2hex(a,max) -- checks alpha input for errors and returns 255-based hex number + a = color.a2norm(a,max) + local hex = string.format("%02X",255*a) + return hex , "#"..tostring(hex) +end +function color.op2hex(op,max) -- checks opacity input for errors and returns 255-based hex number + local a = 1 - color.a2norm(op,max) + local hex = string.format("%02X",255*a) return hex , "#"..tostring(hex) end @@ -382,6 +424,26 @@ function color.convert2mpv(color_space, col_in, sep) return col_conv end +function color.convert2rgba(color_space, col_in, sep, alpha_in) + if sep == nil then sep = ' ' end + if col_in :startswith("#") + or alpha_in:startswith("#") then + if not color_space == "hex" then + print("color type mismatch: passed hex color/alpha ("..col_in.."/"..alpha_in.."), but non-hex color space ("..color_space..")") + color_space = "hex" end end + local col_conv,r,g,b,a + if color_space:lower() == "okhsl" then + local hsl = col_in:splitflex(sep) + r,g,b = color.okhsl2srgb(hsl[1],hsl[2],hsl[3]) + elseif color_space:lower() == "hsl" then + local hsl = col_in:splitflex(sep) + r,g,b = color.hsl2rgb(hsl[1],hsl[2],hsl[3]) + elseif color_space:lower() == "hex" then + col_conv = color.hex2rev(col_in) + end + return col_conv +end + return { color = color, } From a93e37011c63362c3fe519648033bf3f023b9b14 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Thu, 26 Jan 2023 04:29:39 +0700 Subject: [PATCH 69/81] status line: add OSD text/border color/opacity Allows to set these in a better color space vs the default hex --- scripts/mpvi/status_line.lua | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/scripts/mpvi/status_line.lua b/scripts/mpvi/status_line.lua index 36e2ea8..8180daf 100644 --- a/scripts/mpvi/status_line.lua +++ b/scripts/mpvi/status_line.lua @@ -2,7 +2,8 @@ -- Activate with the commands `status-line-enable`, `status-line-disable`, `status-line-toggle` -- Configure via script-opts/mpvi/status_line.yaml -local std = require "lib/std".std +local std = require "lib/std".std +local color = require "lib/color".color local script_dir = mp.get_script_directory() -- ~/.config/mpv/scripts/mpvi local script_dir_base = std.basename(script_dir) -- mpvi @@ -15,6 +16,12 @@ local opts = { enabled = true, size = 36, margin = 10, + override_mpv_conf = false, + color_space = "okhsl", + border_opacity = "0", + border_color = "0 0 0", + text_opacity = "15", + text_color = "0 100 100", text_top_left = "", text_top_right = "", text_bottom_left = "${filename} [${playlist-pos-1}/${playlist-count}]", @@ -39,6 +46,28 @@ std.read_options_yaml(opts, opt_path_rel, function(c) end end) +local text_opacity = color.op2hex( opts.text_opacity ) +local text_color = color.convert2mpv(opts.color_space, opts.text_color ) +local border_opacity = color.op2hex( opts.border_opacity) +local border_color = color.convert2mpv(opts.color_space, opts.border_color ) +if text_opacity == nil then msg.error("ruler: wrong config text opacity " ..opts.text_opacity ) ; text_opacity = "FF" end +if text_color == nil then msg.error("ruler: wrong config text color " ..opts.text_color ) ; text_color = "FFFFFF" end +if border_opacity == nil then msg.error("ruler: wrong config border opacity " ..opts.border_opacity) ; border_opacity = "FF" end +if border_color == nil then msg.error("ruler: wrong config border color " ..opts.border_color ) ; border_color = "000000" end + +local _set = false +if opts.override_mpv_conf then _set = true +else + local osd_text = mp.get_property("osd-color") + local osd_border = mp.get_property("osd-border-color") + local osd_text_def = "#FFFFFFFF" + local osd_border_def = "#FF000000" + if not osd_text == osd_text_def then _set = true end + if not osd_border == osd_border_def then _set = true end end +if _set then + mp.set_property("osd-color" , "#"..text_opacity ..text_color) + mp.set_property("osd-border-color", "#"..border_opacity..border_color) end + local stale = true local active = false From 45335edf8baca003e6a7ad4beea402ed7fa5ea65 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:40:37 +0700 Subject: [PATCH 70/81] color: add hsv and okhsv --- scripts/mpvi/lib/color.lua | 95 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index 3f78a66..db617c0 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -15,6 +15,10 @@ local function srgb_transfer_function(a) if .0031308 >= a then return 12.92 * a else return 1.055 * a^(.4166666666666667) - .055 end end +local function srgb_transfer_function_inv(a) + if .04045 < a then return ((a + .055) / 1.055)^2.4 + else return a / 12.92 end +end local function oklab2linear_srgb(L,a,b) local l_ = L + 0.3963377774 * a + 0.2158037573 * b local m_ = L - 0.1055613458 * a - 0.0638541728 * b @@ -242,6 +246,7 @@ function color.hsl2norm(h,s,l,slmax) -- checks input for errors and returns 1-ba h = h/360; s = s/100; l = l/100 end + h = clamp(h); s = clamp(s); l = clamp(l) -- avoid 0 values return h,s,l end function color.hsl2rgb(h, s, l) @@ -272,6 +277,37 @@ function color.hsl2rgb(h, s, l) std.round(255*math.min(g,1)), std.round(255*math.min(b,1)) end +function color.hsv2rgb(h, s, v) + h,s,v = color.hsl2norm(h,s,v) + local r,g,b + + local i = math.floor(h * 6) + local f = h * 6 - i + local p = v * (1 - s) + local q = v * (1 - f * s) + local t = v * (1 - (1 - f) * s) + + local remainder = i % 6 + if remainder == 0 then r = v; g = t; b = p + elseif remainder == 1 then r = q; g = v; b = p + elseif remainder == 2 then r = p; g = v; b = t + elseif remainder == 3 then r = p; g = q; b = v + elseif remainder == 4 then r = t; g = p; b = v + elseif remainder == 5 then r = v; g = p; b = q + end + + return -- min to avoid returning values > 255; also round + std.round(255*math.min(r,1)), + std.round(255*math.min(g,1)), + std.round(255*math.min(b,1)) +end + +local eps = 1e-05 +function clamp(x) + if x < eps then return eps + elseif x > 1 - eps then return 1 - eps + else return x end +end function color.okhsl2srgb(h,s,l) h,s,l = color.hsl2norm(h,s,l) @@ -315,6 +351,50 @@ function color.okhsl2srgb(h,s,l) std.round(255*math.min(srgb_transfer_function(g),1)), std.round(255*math.min(srgb_transfer_function(b),1)) end +function color.okhsv2srgb(h,s,v) + h,s,v = color.hsl2norm(h,s,v) + local r,g,b + + local a_ = math.cos(2*math.pi*h); + local b_ = math.sin(2*math.pi*h); + + local ST_max = get_ST_max(a_,b_) + local S_max = ST_max[1] + local S_0 = 0.5 + local T = ST_max[2] + local k = 1 - S_0 / S_max + -- -- + local L_v = 1 - s* S_0/(S_0 + T - T*k*s) + local C_v = s*T*S_0/(S_0 + T - T*k*s) + -- -- + local L = v*L_v + local C = v*C_v + -- to present steps along the way + -- L = v + -- C = v* s*S_max + -- L = v* (1 - s*S_max/(S_max+T)) + -- C = v*T* s*S_max/(S_max+T) + + local L_vt = toe_inv(L_v) + local C_vt = C_v * L_vt / L_v + + local L_new = toe_inv(L) -- * L_v/L_vt + C = C * L_new/L + L = L_new + + local rs,gs,bs = oklab2linear_srgb(L_vt, a_*C_vt, b_*C_vt) + local scale_L = (1 / (math.max(rs,gs,bs,0)))^(1/3) + + -- remove to see effect without rescaling + L = L * scale_L + C = C * scale_L + + r,g,b = oklab2linear_srgb(L, C*a_, C*b_) + return -- min to avoid returning values > 255; also round + std.round(255*math.min(srgb_transfer_function(r),1)), + std.round(255*math.min(srgb_transfer_function(g),1)), + std.round(255*math.min(srgb_transfer_function(b),1)) +end function color.hex2rgb(hex) -- also converts short rgbs @@ -414,10 +494,18 @@ function color.convert2mpv(color_space, col_in, sep) local hsl = col_in:splitflex(sep) local r,g,b = color.okhsl2srgb(hsl[1],hsl[2],hsl[3]) col_conv = color.rgb2hexbgr(r,g,b) + elseif color_space:lower() == "okhsv" then + local hsv = col_in:splitflex(sep) + local r,g,b = color.okhsv2srgb(hsv[1],hsv[2],hsv[3]) + col_conv = color.rgb2hexbgr(r,g,b) elseif color_space:lower() == "hsl" then local hsl = col_in:splitflex(sep) local r,g,b = color.hsl2rgb(hsl[1],hsl[2],hsl[3]) col_conv = color.rgb2hexbgr(r,g,b) + elseif color_space:lower() == "hsv" then + local hsv = col_in:splitflex(sep) + local r,g,b = color.hsv2rgb(hsv[1],hsv[2],hsv[3]) + col_conv = color.rgb2hexbgr(r,g,b) elseif color_space:lower() == "hex" then col_conv = color.hex2rev(col_in) end @@ -435,9 +523,16 @@ function color.convert2rgba(color_space, col_in, sep, alpha_in) if color_space:lower() == "okhsl" then local hsl = col_in:splitflex(sep) r,g,b = color.okhsl2srgb(hsl[1],hsl[2],hsl[3]) + elseif color_space:lower() == "okhsv" then + local hsv = col_in:splitflex(sep) + r,g,b = color.okhsv2srgb(hsv[1],hsv[2],hsv[3]) elseif color_space:lower() == "hsl" then local hsl = col_in:splitflex(sep) r,g,b = color.hsl2rgb(hsl[1],hsl[2],hsl[3]) + elseif color_space:lower() == "hsv" then + local hsv = col_in:splitflex(sep) + r,g,b = color.hsv2rgb(hsv[1],hsv[2],hsv[3]) + a = color.a2hex(alpha_in) elseif color_space:lower() == "hex" then col_conv = color.hex2rev(col_in) end From ca291d1fc36e2e5761e6f2d8560222e6154fb5bf Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:43:14 +0700 Subject: [PATCH 71/81] color: fix order bug in rgb2norm --- scripts/mpvi/lib/color.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index db617c0..229e3e9 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -428,7 +428,7 @@ function color.hex2rev(hex) else print("wrong input length ("..len.."), should be 2, 3, or 6 symbols without #: "..hex) end return hexbgr, "#"..tostring(hexbgr) end -function color.rgb2norm(r,b,g,max) -- checks input for errors and returns 255-based numbers +function color.rgb2norm(r,g,b,max) -- checks input for errors and returns 255-based numbers local norm_str = 0 -- normalize str→num if type(r) == 'string' then r = tonumber(r); norm_str = norm_str + 1 end if type(g) == 'string' then g = tonumber(g); norm_str = norm_str + 1 end From 9639884c1d8ce8d588ef5e317d3e9cf2458ccc66 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:44:35 +0700 Subject: [PATCH 72/81] color: add alpha to convert2rgba --- scripts/mpvi/lib/color.lua | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index 229e3e9..e8ba8e8 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -523,20 +523,24 @@ function color.convert2rgba(color_space, col_in, sep, alpha_in) if color_space:lower() == "okhsl" then local hsl = col_in:splitflex(sep) r,g,b = color.okhsl2srgb(hsl[1],hsl[2],hsl[3]) + a = color.a2hex(alpha_in) elseif color_space:lower() == "okhsv" then local hsv = col_in:splitflex(sep) r,g,b = color.okhsv2srgb(hsv[1],hsv[2],hsv[3]) + a = color.a2hex(alpha_in) elseif color_space:lower() == "hsl" then local hsl = col_in:splitflex(sep) r,g,b = color.hsl2rgb(hsl[1],hsl[2],hsl[3]) + a = color.a2hex(alpha_in) elseif color_space:lower() == "hsv" then local hsv = col_in:splitflex(sep) r,g,b = color.hsv2rgb(hsv[1],hsv[2],hsv[3]) a = color.a2hex(alpha_in) elseif color_space:lower() == "hex" then - col_conv = color.hex2rev(col_in) + r,g,b = color.hex2rgb(col_in) end - return col_conv + + return r,g,b,a end return { From 4fabda9a2e09476aca68003bf5c798fad05df017 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:45:43 +0700 Subject: [PATCH 73/81] rename convert2mpv to convert2ass --- scripts/mpvi/lib/color.lua | 2 +- scripts/mpvi/minimap.lua | 4 ++-- scripts/mpvi/ruler.lua | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index e8ba8e8..48acc56 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -483,7 +483,7 @@ function color.op2hex(op,max) -- checks opacity input for errors and returns 255 return hex , "#"..tostring(hex) end -function color.convert2mpv(color_space, col_in, sep) +function color.convert2ass(color_space, col_in, sep) if sep == nil then sep = ' ' end if col_in:startswith("#") then if not color_space == "hex" then diff --git a/scripts/mpvi/minimap.lua b/scripts/mpvi/minimap.lua index a13b2f0..096393a 100644 --- a/scripts/mpvi/minimap.lua +++ b/scripts/mpvi/minimap.lua @@ -38,8 +38,8 @@ std.read_options_yaml(opts, opt_path_rel, function(c) mark_stale() end) -local image_color = color.convert2mpv(opts.color_space, opts.image_color ) -local view_color = color.convert2mpv(opts.color_space, opts.view_color ) +local image_color = color.convert2ass(opts.color_space, opts.image_color ) +local view_color = color.convert2ass(opts.color_space, opts.view_color ) local image_opacity = color.a2hex( opts.image_opacity) local view_opacity = color.a2hex( opts.view_opacity ) if image_color == nil then msg.error("ruler: wrong config image color " ..opts.image_color); image_color = "BBBBBB" end diff --git a/scripts/mpvi/ruler.lua b/scripts/mpvi/ruler.lua index 67677ed..ff67c1a 100644 --- a/scripts/mpvi/ruler.lua +++ b/scripts/mpvi/ruler.lua @@ -46,7 +46,7 @@ std.read_options_yaml(opts, opt_path_rel, function() if state ~= 0 then add_bindings(); mark_stale() end end) -local line_color = color.convert2mpv(opts.color_space, opts.line_color) +local line_color = color.convert2ass(opts.color_space, opts.line_color) if line_color == nil then msg.error("ruler: wrong config line color "..opts.line_color); line_color = "000000" end local ov = mp.create_osd_overlay("ass-events") From f89d0ae8d7ab62cb9e6e10bbff6a8478490154ab Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:46:27 +0700 Subject: [PATCH 74/81] add okhsv/hsv to config comments --- script-opts/mpvi/minimap.yaml | 2 +- script-opts/mpvi/ruler.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/script-opts/mpvi/minimap.yaml b/script-opts/mpvi/minimap.yaml index 97df826..312e4eb 100644 --- a/script-opts/mpvi/minimap.yaml +++ b/script-opts/mpvi/minimap.yaml @@ -3,7 +3,7 @@ enabled : true # whether to show by default center : "92 92" # the position of the center of the minimap, in percentage of the window (x, y) scale : 12 # the scale of the minimap (i.e. the view rectangle is scale / 100 times the size of the window) max_size : "16 16" # the cutoff size of the minimap (i.e. the image rectangle is clipped if it falls outside of the this zone) -color_space : "okhsl" # "okhsl", "hsl", or "hex" bottosson.github.io/misc/colorpicker +color_space : "okhsl" # "okhsl","okhsv", "hsl","hsv" or "hex" bottosson.github.io/misc/colorpicker image_opacity : "50" # opacity of the "image" (from 0=opaque to 100=transparent) image_color : "0 0 75" # color of the "image" view_opacity : "100" # opacity of the "view" diff --git a/script-opts/mpvi/ruler.yaml b/script-opts/mpvi/ruler.yaml index fd2442d..c3b6207 100644 --- a/script-opts/mpvi/ruler.yaml +++ b/script-opts/mpvi/ruler.yaml @@ -6,7 +6,7 @@ show_angles : "degrees" # can be "degrees", "radians", "bo line_width : 2 # dots_radius : 3 # font_size : 36 # -color_space : "okhsl" # "okhsl", "hsl", or "hex" bottosson.github.io/misc/colorpicker +color_space : "okhsl" # "okhsl","okhsv", "hsl","hsv" or "hex" bottosson.github.io/misc/colorpicker line_color : "57 100 70" # confirm_bindings : "MBTN_LEFT ENTER" # bindings used to set points. The binding to trigger ruler mode can also be used. Space/comma-separated list exit_bindings : "ESC" # bindings used to set points. The binding to trigger ruler mode can also be used. Space/comma-separated list From e9a846b30ec18c55ad6ebde2e1df5481d57c2663 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:46:43 +0700 Subject: [PATCH 75/81] color: add convert2hex --- scripts/mpvi/lib/color.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/mpvi/lib/color.lua b/scripts/mpvi/lib/color.lua index 48acc56..c0d6e54 100644 --- a/scripts/mpvi/lib/color.lua +++ b/scripts/mpvi/lib/color.lua @@ -511,6 +511,9 @@ function color.convert2ass(color_space, col_in, sep) end return col_conv end +function color.convert2hex(color_space, col_in, sep) + return color.hex2rev(color.convert2ass(color_space, col_in, sep)) +end function color.convert2rgba(color_space, col_in, sep, alpha_in) if sep == nil then sep = ' ' end From 4e0abe3ad87f69fd2bb8c265e8a5a21d1400bca5 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:47:53 +0700 Subject: [PATCH 76/81] color: add license --- scripts/mpvi/lib/color | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 scripts/mpvi/lib/color diff --git a/scripts/mpvi/lib/color b/scripts/mpvi/lib/color new file mode 100644 index 0000000..ff0237f --- /dev/null +++ b/scripts/mpvi/lib/color @@ -0,0 +1,16 @@ +Copyright (c) 2020 Björn Ottosson +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From aadf34ec5c4736ff50e6c5167b9d13a9b447712a Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:48:35 +0700 Subject: [PATCH 77/81] status line: fix OSD color order default override --- scripts/mpvi/status_line.lua | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/scripts/mpvi/status_line.lua b/scripts/mpvi/status_line.lua index 8180daf..c147917 100644 --- a/scripts/mpvi/status_line.lua +++ b/scripts/mpvi/status_line.lua @@ -46,10 +46,11 @@ std.read_options_yaml(opts, opt_path_rel, function(c) end end) +-- OSD accepts #AARRGGBB instead of BGR in ass local text_opacity = color.op2hex( opts.text_opacity ) -local text_color = color.convert2mpv(opts.color_space, opts.text_color ) +local text_color = color.convert2hex(opts.color_space, opts.text_color ) local border_opacity = color.op2hex( opts.border_opacity) -local border_color = color.convert2mpv(opts.color_space, opts.border_color ) +local border_color = color.convert2hex(opts.color_space, opts.border_color ) if text_opacity == nil then msg.error("ruler: wrong config text opacity " ..opts.text_opacity ) ; text_opacity = "FF" end if text_color == nil then msg.error("ruler: wrong config text color " ..opts.text_color ) ; text_color = "FFFFFF" end if border_opacity == nil then msg.error("ruler: wrong config border opacity " ..opts.border_opacity) ; border_opacity = "FF" end @@ -57,13 +58,13 @@ if border_color == nil then msg.error("ruler: wrong config border color " local _set = false if opts.override_mpv_conf then _set = true -else +else -- override even if ↑ false when OSD is not set (values=defaults) local osd_text = mp.get_property("osd-color") local osd_border = mp.get_property("osd-border-color") local osd_text_def = "#FFFFFFFF" local osd_border_def = "#FF000000" - if not osd_text == osd_text_def then _set = true end - if not osd_border == osd_border_def then _set = true end end + if osd_text == osd_text_def then _set = true end + if osd_border == osd_border_def then _set = true end end if _set then mp.set_property("osd-color" , "#"..text_opacity ..text_color) mp.set_property("osd-border-color", "#"..border_opacity..border_color) end From 5f010dee630351b8bcbd7b09cc544bdcaf7f15d6 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 01:49:08 +0700 Subject: [PATCH 78/81] status line: update config --- script-opts/mpvi/status_line.yaml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/script-opts/mpvi/status_line.yaml b/script-opts/mpvi/status_line.yaml index 6cb97f4..199ee15 100644 --- a/script-opts/mpvi/status_line.yaml +++ b/script-opts/mpvi/status_line.yaml @@ -2,10 +2,16 @@ enabled : true # whether to show by default size : 36 # font size margin : 10 # distance of the text to the borders +override_mpv_conf : false # override OSD color set in mpv.conf (default values are always overriden) +color_space : "okhsl" # "okhsl","okhsv", "hsl","hsv" or "hex" bottosson.github.io/misc/colorpicker +border_opacity : "0" # opacity of the OSD border (from 0=opaque to 100=transparent) +border_color : "0 0 0" # color of the OSD border +text_opacity : "15" # opacity of the OSD text (from 0=opaque to 100=transparent) +text_color : "0 100 100" # color of the OSD text # ↓ text to be expanded # details @ mpv.io/manual/master -# line break \N # -# ass tags → # docs.aegisub.org/3.2/ASS_Tags/ +# \N # line break +# ass tags # docs.aegisub.org/3.2/ASS_Tags/ text_top_left : "" text_top_right : "" text_bottom_left : "${filename} [${playlist-pos-1}/${playlist-count}]" -text_bottom_right : "[${dwidth:X}x${dheight:X}]" +text_bottom_right : "[${dwidth:X}×${dheight:X}]" From ccdbc8ec932f04649c16c3dc73508e043e7cc440 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 02:06:28 +0700 Subject: [PATCH 79/81] add explicit profile to mpv.conf and reformat to tabular view --- mpv.conf | 83 ++++++++++++++++++++++++-------------------------------- 1 file changed, 35 insertions(+), 48 deletions(-) diff --git a/mpv.conf b/mpv.conf index 8e65bdd..6b21c40 100644 --- a/mpv.conf +++ b/mpv.conf @@ -1,48 +1,35 @@ -## IMAGE -# classic opengl-hq parameter, change at will -scale=spline36 -cscale=spline36 -dscale=mitchell -dither-depth=auto -correct-downscaling -sigmoid-upscaling -# debanding seems rarely useful with images -#deband -# dark grey background instead of pure black -background=0.2 - -## MISC -mute=yes -# the osc is mostly useful for videos -osc=no -# don't try to autoload subtitles or audio files -sub-auto=no -audio-file-auto=no -# get rid of the useless V: 00:00:00 / 00:00:00 line -term-status-msg= - -# replace mpv with mvi in the window title -title="${?media-title:${media-title}}${!media-title:No file} - mvi" - -# don't slideshow by default -image-display-duration=inf -# loop files in case of webms or gifs -loop-file=inf -# and loop the whole playlist -loop-playlist=inf - -# you need this if you plan to use drag-to-pan or pan-follows-cursor with MOUSE_LEFT -window-dragging=no - -#according to haasn, aspect ratio info for PNG and JPG is "universally bust" -[extension.png] -video-aspect-override=no - -[extension.jpg] -video-aspect-override=no - -[extension.jpeg] -profile=extension.jpg - -[silent] -msg-level=all=no +[mpvi] # MVImage viewer Profile +profile-restore = copy # on load backup values of all profile options to allow restore +# classic opengl-hq parameter # change at will +scale = spline36 # +cscale = spline36 # +dscale = mitchell # +dither-depth = auto # +correct-downscaling # +sigmoid-upscaling # +#deband # debanding seems rarely useful with images +background = 0.2 # dark grey background instead of pure black + +# Misc +mute = yes # +osc = no # the osc is mostly useful for videos +sub-auto = no # don't try to autoload subtitles or audio files +audio-file-auto = no # +term-status-msg = # get rid of the useless V: 00:00:00 / 00:00:00 line + +title = "${?media-title:${media-title}}${!media-title:No file} - mvi" # replace mpv with mvi in the window title + +image-display-duration = inf # don't slideshow by default +loop-file = inf # loop files in case of webms or gifs +loop-playlist = inf # and loop the whole playlist + +window-dragging = no # if you plan to use drag-to-pan or pan-follows-cursor with MOUSE_LEFT + +[extension.png] # +video-aspect-override = no # @haasn, aspect ratio info for PNG and JPG is "universally bust" +[extension.jpg] # +video-aspect-override = no # +[extension.jpeg] # +profile = extension.jpg # +[silent] # +msg-level = all=no # From 191cbc20e4c828218618319b100e8e5c5ad550a7 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Fri, 27 Jan 2023 02:21:53 +0700 Subject: [PATCH 80/81] add profile to input.conf for conditional loading reformat to tabular view add more image movement commands --- input.conf | 189 +++++++++++++++++++++++++++-------------------------- 1 file changed, 98 insertions(+), 91 deletions(-) diff --git a/input.conf b/input.conf index 47e06cb..91e881a 100644 --- a/input.conf +++ b/input.conf @@ -1,105 +1,112 @@ -1 change-list script-opts append image_positioning-drag_to_pan_margin=200 -2 change-list script-opts append ruler-exit_bindings=8 -3 change-list script-opts append ruler-line_color=FF -4 change-list script-opts append ruler-scale=25 -5 change-list script-opts append ruler-max_size=20,20 - -SPACE repeatable playlist-next -alt+SPACE repeatable playlist-prev - -UP ignore -DOWN ignore -LEFT repeatable playlist-prev -RIGHT repeatable playlist-next - -# simple reminder of default bindings -#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 +#↓ enable to allow explicitly toggling MPVI input section +#w enable-section "mpvi"; print-text "mpvi ON" # test +#————— Input Section: MVImage —————# +#w {mpvi} disable-section "mpvi"; print-text "mpvi OFF" # test + +# |default| +1 {mpvi} change-list script-opts append image_positioning-drag_to_pan_margin=200 #|add contrast -1| +2 {mpvi} change-list script-opts append ruler-exit_bindings=8 #|add contrast 1| +3 {mpvi} change-list script-opts append ruler-line_color=FF #|add brightness -1| +4 {mpvi} change-list script-opts append ruler-scale=25 #|add brightness 1| +5 {mpvi} change-list script-opts append ruler-max_size=20,20 #|add gamma -1| +#6 #|add gamma 1| +#7 #|add saturation -1| +#8 #|add saturation 1| + +SPACE {mpvi} repeatable playlist-next # +alt+SPACE {mpvi} repeatable playlist-prev # + +UP {mpvi} ignore # +DOWN {mpvi} ignore # +#LEFT {mpvi} repeatable playlist-prev # +#RIGHT {mpvi} repeatable playlist-next # # mouse-centric bindings -MBTN_RIGHT script-binding drag-to-pan -MBTN_LEFT script-binding pan-follows-cursor -MBTN_LEFT_DBL ignore -WHEEL_UP script-message cursor-centric-zoom 0.1 -WHEEL_DOWN script-message cursor-centric-zoom -0.1 +MBTN_LEFT {mpvi} script-binding pan-follows-cursor # +MBTN_RIGHT {mpvi} script-binding drag-to-pan # +MBTN_LEFT_DBL {mpvi} ignore # +WHEEL_UP {mpvi} script-message cursor-centric-zoom 0.1 # +WHEEL_DOWN {mpvi} script-message cursor-centric-zoom -0.1 # # panning with the keyboard: -# pan-image takes the following arguments -# pan-image AXIS AMOUNT ZOOM_INVARIANT IMAGE_CONSTRAINED -# ^ ^ ^ -# x or y | | -# | | -# if yes, will pan by the same if yes, stops panning if the image -# amount regardless of zoom would go outside of the window - -ctrl+left repeatable script-message pan-image x -0.1 yes yes -ctrl+right repeatable script-message pan-image x +0.1 yes yes -ctrl+up repeatable script-message pan-image y +0.1 yes yes -ctrl+down repeatable script-message pan-image y -0.1 yes yes - -# now with more precision -alt+left repeatable script-message pan-image x -0.01 yes yes -alt+right repeatable script-message pan-image x +0.01 yes yes -alt+up repeatable script-message pan-image y +0.01 yes yes -alt+down repeatable script-message pan-image y -0.01 yes yes - -# replace at will with h,j,k,l if you prefer vim-style bindings - -# on a trackpad you may want to use these -#WHEEL_LEFT repeatable script-message pan-image x +0.02 yes yes -#WHEEL_RIGHT repeatable script-message pan-image x -0.02 yes yes -#WHEEL_UP repeatable script-message pan-image y -0.02 yes yes -#WHEEL_DOWN repeatable script-message pan-image y +0.02 yes yes - -# align the border of the image to the border of the window -# align-border takes the following arguments: -# align-border ALIGN_X ALIGN_Y +# pan-image AXIS AMOUNT ZOOM_INVARIANT IMAGE_CONSTRAINED # panning with the keyboard +# ↑x/y ↑if yes, pan by the same amount regardless of zoom +# ↑if yes, stops panning if the image would go outside of the window +left {mpvi} repeatable script-message pan-image x -0.1 yes yes # +right {mpvi} repeatable script-message pan-image x +0.1 yes yes # +up {mpvi} repeatable script-message pan-image y +0.1 yes yes # +down {mpvi} repeatable script-message pan-image y -0.1 yes yes # +# ↑ with more precision # +alt+left {mpvi} repeatable script-message pan-image x -0.01 yes yes # +alt+right {mpvi} repeatable script-message pan-image x +0.01 yes yes # +alt+up {mpvi} repeatable script-message pan-image y +0.01 yes yes # +alt+down {mpvi} repeatable script-message pan-image y -0.01 yes yes # +# ↑ with less precision # +ctrl+left {mpvi} repeatable script-message pan-image x -0.25 yes yes # +ctrl+right {mpvi} repeatable script-message pan-image x +0.25 yes yes # +ctrl+up {mpvi} repeatable script-message pan-image y +0.25 yes yes # +ctrl+down {mpvi} repeatable script-message pan-image y -0.25 yes yes # +# Unconstrained # +Shift+left {mpvi} repeatable script-message pan-image x -0.1 yes no # +Shift+right {mpvi} repeatable script-message pan-image x +0.1 yes no # +Shift+up {mpvi} repeatable script-message pan-image y +0.1 yes no # +Shift+down {mpvi} repeatable script-message pan-image y -0.1 yes no # +# ↑ with more precision # +Shift+alt+left {mpvi} repeatable script-message pan-image x -0.01 yes no # +Shift+alt+right {mpvi} repeatable script-message pan-image x +0.01 yes no # +Shift+alt+up {mpvi} repeatable script-message pan-image y +0.01 yes no # +Shift+alt+down {mpvi} repeatable script-message pan-image y -0.01 yes no # +# ↑ with less precision # +Shift+ctrl+left {mpvi} repeatable script-message pan-image x -0.25 yes no # +Shift+ctrl+right {mpvi} repeatable script-message pan-image x +0.25 yes no # +Shift+ctrl+up {mpvi} repeatable script-message pan-image y +0.25 yes no # +Shift+ctrl+down {mpvi} repeatable script-message pan-image y -0.25 yes no # +# trackpad may want to use these # +WHEEL_LEFT {mpvi} repeatable script-message pan-image x -0.25 yes yes # +WHEEL_RIGHT {mpvi} repeatable script-message pan-image x +0.25 yes yes # +WHEEL_UP {mpvi} repeatable script-message pan-image y -0.25 yes yes # +WHEEL_DOWN {mpvi} repeatable script-message pan-image y +0.25 yes yes # +# ↑ no limit (Mac-bugged) NOT working with modifiers # +Shift+WHEEL_LEFT {mpvi} repeatable script-message pan-image x -0.25 yes no # +Shift+WHEEL_RIGHT {mpvi} repeatable script-message pan-image x +0.25 yes no # +Shift+WHEEL_UP {mpvi} repeatable script-message pan-image y -0.25 yes no # +Shift+WHEEL_DOWN {mpvi} repeatable script-message pan-image y +0.25 yes no # + + +# align-border ALIGN_X ALIGN_Y # align the border of the image to the border of the window # any value for ALIGN_* is accepted, -1 and 1 map to the border of the window -ctrl+shift+left script-message align-border -1 "" -ctrl+shift+right script-message align-border 1 "" -ctrl+shift+up script-message align-border "" 1 -ctrl+shift+down script-message align-border "" -1 +ctrl+shift+r {mpvi} script-message align-border 0 0 # reset +ctrl+shift+left {mpvi} script-message align-border -1 '' # +ctrl+shift+right {mpvi} script-message align-border 1 '' # +ctrl+shift+up {mpvi} script-message align-border '' 1 # +ctrl+shift+down {mpvi} script-message align-border '' -1 # +#ctrl+shift+left {mpvi} script-message align-border -0.5 "" # +#ctrl+shift+right {mpvi} script-message align-border 0.5 "" # +#ctrl+shift+up {mpvi} script-message align-border "" 0.5 # +#ctrl+shift+down {mpvi} script-message align-border "" -0.5 # -# reset the image -ctrl+0 no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0 +ctrl+0 {mpvi} no-osd set video-pan-x 0; no-osd set video-pan-y 0; no-osd set video-zoom 0 # reset the image -+ add video-zoom 0.5 -- add video-zoom -0.5; script-message reset-pan-if-visible -= no-osd set video-zoom 0; script-message reset-pan-if-visible ++ {mpvi} add video-zoom 0.5 # +- {mpvi} add video-zoom -0.5 ; script-message reset-pan-if-visible # += {mpvi} no-osd set video-zoom 0 ; script-message reset-pan-if-visible # -e script-message equalizer-toggle -alt+e script-message equalizer-reset +e {mpvi} script-message equalizer-toggle +alt+e {mpvi} script-message equalizer-reset -h no-osd vf toggle hflip; show-text "Horizontal flip" -v no-osd vf toggle vflip; show-text "Vertical flip" +h {mpvi} no-osd vf toggle hflip; show-text "Horizontal flip" +v {mpvi} no-osd vf toggle vflip; show-text "Vertical flip" -r script-message rotate-video 90; show-text "Clockwise rotation" -R script-message rotate-video -90; show-text "Counter-clockwise rotation" -alt+r no-osd set video-rotate 0; show-text "Reset rotation" +r {mpvi} script-message rotate-video 90; show-text "Clockwise rotation" # +R {mpvi} script-message rotate-video -90; show-text "Counter-clockwise rotation" # +alt+r {mpvi} no-osd set video-rotate 0; show-text "Reset rotation" # -d script-message ruler +d {mpvi} script-message ruler -# Toggling between pixel-exact reproduction and interpolation -a cycle-values scale nearest ewa_lanczossharp +a {mpvi} cycle-values scale nearest ewa_lanczossharp # Toggling between pixel-exact reproduction and interpolation +c {mpvi} cycle icc-profile-auto # Toggle color management on or off +S {mpvi} screenshot window # Screenshot of the window output -# Toggle color management on or off -c cycle icc-profile-auto +A {mpvi} cycle-values video-aspect-override "-1" "no" # Toggle aspect ratio information on and off -# Screenshot of the window output -S screenshot window - -# Toggle aspect ratio information on and off -A cycle-values video-aspect-override "-1" "no" - -p script-message force-print-filename - -# ADVANCED: you can define bindings that belong to a "section" (named "image-viewer" here) like so: -#alt+SPACE {image-viewer} repeatable playlist-prev -#SPACE {image-viewer} repeatable playlist-next -# to load them conditionally with a command. See scripts-opts/image_viewer.conf for how you can do this +p {mpvi} script-message force-print-filename From 3f4c5483543e1ceb02fbe78acb76cb48a42b5369 Mon Sep 17 00:00:00 2001 From: eugenesvk Date: Sat, 28 Jan 2023 00:47:45 +0700 Subject: [PATCH 81/81] minimap: fix the view configured to be invisible --- script-opts/mpvi/minimap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script-opts/mpvi/minimap.yaml b/script-opts/mpvi/minimap.yaml index 312e4eb..8380c3e 100644 --- a/script-opts/mpvi/minimap.yaml +++ b/script-opts/mpvi/minimap.yaml @@ -6,7 +6,7 @@ max_size : "16 16" # the cutoff size of the minimap (i.e. t color_space : "okhsl" # "okhsl","okhsv", "hsl","hsv" or "hex" bottosson.github.io/misc/colorpicker image_opacity : "50" # opacity of the "image" (from 0=opaque to 100=transparent) image_color : "0 0 75" # color of the "image" -view_opacity : "100" # opacity of the "view" +view_opacity : "75" # opacity of the "view" view_color : "0 0 15" # color of the "view" view_above_image : true # whether the view should be drawn above the image hide_when_full_image_in_view : true # whether to show the minimap if the current image is fully visible