diff options
| author | dyknon dyknonr5fjp | 2026-01-11 23:51:38 +0900 |
|---|---|---|
| committer | dyknon dyknonr5fjp | 2026-01-11 23:51:38 +0900 |
| commit | ec5dcc4310aba25e3cf3276739063c1c5cd01c23 (patch) | |
| tree | 565e82f10c6638d0cb9c17798a2ffa45ed5e4f3d | |
| parent | b7bc3f0d4488b6822506b9f93121249d078c38e3 (diff) | |
| parent | bd601414ddd4be8cf89e79451e5bc68d02f871b0 (diff) | |
Merge branch 'orig-osc' (import changes from mpv-0.41)
| -rw-r--r-- | LICENSE.GPL | 9 | ||||
| -rw-r--r-- | LICENSE.LGPL | 27 | ||||
| -rw-r--r-- | README | 3 | ||||
| -rw-r--r-- | osc-mod.lua | 921 |
4 files changed, 451 insertions, 509 deletions
diff --git a/LICENSE.GPL b/LICENSE.GPL index d159169..9efa6fb 100644 --- a/LICENSE.GPL +++ b/LICENSE.GPL @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + with this program; if not, see <https://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. @@ -329,8 +328,8 @@ necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. - <signature of Ty Coon>, 1 April 1989 - Ty Coon, President of Vice + <signature of Moe Ghoul>, 1 April 1989 + Moe Ghoul, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may diff --git a/LICENSE.LGPL b/LICENSE.LGPL index 4362b49..d35234e 100644 --- a/LICENSE.LGPL +++ b/LICENSE.LGPL @@ -2,7 +2,7 @@ Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + <https://fsf.org/> Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -55,7 +55,7 @@ modified by someone else and passed on, the recipients should know that what they have is not the original version, so that the original author's reputation will not be affected by problems that might be introduced by others. - + Finally, software patents pose a constant threat to the existence of any free program. We wish to make sure that a company cannot effectively restrict the users of a free program by obtaining a @@ -111,7 +111,7 @@ modification follow. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". The former contains code derived from the library, whereas the latter must be combined with the library in order to run. - + GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION @@ -158,7 +158,7 @@ Library. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. - + 2. You may modify your copy or copies of the Library or any portion of it, thus forming a work based on the Library, and copy and distribute such modifications or work under the terms of Section 1 @@ -216,7 +216,7 @@ instead of to this License. (If a newer version than version 2 of the ordinary GNU General Public License has appeared, then you can specify that version instead if you wish.) Do not make any other change in these notices. - + Once this change is made in a given copy, it is irreversible for that copy, so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. @@ -267,7 +267,7 @@ Library will still fall under Section 6.) distribute the object code for the work under the terms of Section 6. Any executables containing that work also fall under Section 6, whether or not they are linked directly with the Library itself. - + 6. As an exception to the Sections above, you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library, and distribute that work @@ -329,7 +329,7 @@ restrictions of other proprietary libraries that do not normally accompany the operating system. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. - + 7. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License, and distribute such a combined @@ -370,7 +370,7 @@ subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties with this License. - + 11. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or @@ -422,7 +422,7 @@ conditions either of that version or of any later version published by the Free Software Foundation. If the Library does not specify a license version number, you may choose any version ever published by the Free Software Foundation. - + 14. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these, write to the author to ask for permission. For software which is @@ -456,7 +456,7 @@ SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS - + How to Apply These Terms to Your New Libraries If you develop a new library, and you want it to be of the greatest @@ -484,8 +484,7 @@ convey the exclusion of warranty; and each file should have at least the Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + License along with this library; if not, see <https://www.gnu.org/licenses/>. Also add information on how to contact you by electronic and paper mail. @@ -496,7 +495,7 @@ necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker. - <signature of Ty Coon>, 1 April 1990 - Ty Coon, President of Vice + <signature of Moe Ghoul>, 1 April 1990 + Moe Ghoul, President of Vice That's all there is to it! @@ -15,8 +15,7 @@ cp ytdl-storyboard.so osc-mod.lua ~/.config/mpv/scripts Copyright dyknon 2025, 2026 osc-mod.lua is originally written by mpv developers. -It was taken from mpv 0.39.0. -Look at mpv commit history to know original authors of this file. +It was taken from mpv 0.41.0. Check tag: mpv-0.41.0-osc-mod. There are small changes by dyknon to: * Export information about user interactins with osc to other scripts. diff --git a/osc-mod.lua b/osc-mod.lua index bf4ce9d..f5af003 100644 --- a/osc-mod.lua +++ b/osc-mod.lua @@ -1,7 +1,6 @@ local assdraw = require 'mp.assdraw' local msg = require 'mp.msg' local opt = require 'mp.options' -local utils = require 'mp.utils' -- -- Parameters @@ -23,13 +22,11 @@ local user_opts = { hidetimeout = 500, -- duration in ms until the OSC hides if no -- mouse movement. enforced non-negative for the -- user, but internally negative is "always-on". - fadeduration = 200, -- duration of fade out in ms, 0 = no fade + fadeduration = 200, -- duration of fade out (and fade in, if enabled) in ms, 0 = no fade + fadein = false, -- whether to enable fade-in effect deadzonesize = 0.5, -- size of deadzone minmousemove = 0, -- minimum amount of pixels the mouse has to -- move between ticks to make the OSC show up - iamaprogrammer = false, -- use native mpv values and disable OSC - -- internal track list management (and some - -- functions that depend on it) layout = "bottombar", seekbarstyle = "bar", -- bar, diamond or knob seekbarhandlesize = 0.6, -- size ratio of the diamond and knob handle @@ -38,7 +35,7 @@ local user_opts = { seekrangealpha = 200, -- transparency of seekranges seekbarkeyframes = true, -- use keyframes when dragging the seekbar scrollcontrols = true, -- allow scrolling when hovering certain OSC elements - title = "${media-title}", -- string compatible with property-expansion + title = "${!playlist-count==1:[${playlist-pos-1}/${playlist-count}] }${media-title}", -- to be shown as OSC title tooltipborder = 1, -- border of tooltip in bottom/topbar timetotal = false, -- display total time instead of remaining time? @@ -47,6 +44,7 @@ local user_opts = { timems = false, -- display timecodes with milliseconds? tcspace = 100, -- timecode spacing (compensate font size estimation) visibility = "auto", -- only used at init to set visibility_mode(...) + visibility_modes = "never_auto_always", -- visibility modes to cycle through boxmaxchars = 80, -- title crop threshold for box layout boxvideo = false, -- apply osc_param.video_margins to video windowcontrols = "auto", -- whether to show window controls @@ -54,8 +52,6 @@ local user_opts = { windowcontrols_title = "${media-title}", -- same as title but for windowcontrols greenandgrumpy = false, -- disable santa hat livemarkers = true, -- update seekbar chapter markers on duration change - chapters_osd = true, -- whether to show chapters OSD on next/prev - playlist_osd = true, -- whether to show playlist OSD on next/prev chapter_fmt = "Chapter: %s", -- chapter print format for seekbar-hover. "no" to disable unicodeminus = false, -- whether to use the Unicode minus sign character @@ -71,8 +67,101 @@ local user_opts = { time_pos_outline_color = "#000000", -- color of the border timecodes in slimbox and TimePosBar - tick_delay = 1 / 60, -- minimum interval between OSC redraws in seconds - tick_delay_follow_display_fps = false -- use display fps as the minimum interval + tick_delay = 1 / 60, -- minimum interval between OSC redraws in seconds + tick_delay_follow_display_fps = false, -- use display fps as the minimum interval + + -- luacheck: push ignore + -- luacheck: max line length + menu_mbtn_left_command = "script-binding select/menu; script-message-to osc osc-hide", + menu_mbtn_mid_command = "", + menu_mbtn_right_command = "", + + playlist_prev_mbtn_left_command = "playlist-prev", + playlist_prev_mbtn_mid_command = "show-text ${playlist} 3000", + playlist_prev_mbtn_right_command = "script-binding select/select-playlist; script-message-to osc osc-hide", + + playlist_next_mbtn_left_command = "playlist-next", + playlist_next_mbtn_mid_command = "show-text ${playlist} 3000", + playlist_next_mbtn_right_command = "script-binding select/select-playlist; script-message-to osc osc-hide", + + title_mbtn_left_command = "script-binding stats/display-page-5", + title_mbtn_mid_command = "show-text ${path}", + title_mbtn_right_command = "script-binding select/select-watch-history; script-message-to osc osc-hide", + + play_pause_mbtn_left_command = "cycle pause", + play_pause_mbtn_mid_command = "cycle-values loop-playlist inf no", + play_pause_mbtn_right_command = "cycle-values loop-file inf no", + + chapter_prev_mbtn_left_command = "osd-msg add chapter -1", + chapter_prev_mbtn_mid_command = "show-text ${chapter-list} 3000", + chapter_prev_mbtn_right_command = "script-binding select/select-chapter; script-message-to osc osc-hide", + + chapter_next_mbtn_left_command = "osd-msg add chapter 1", + chapter_next_mbtn_mid_command = "show-text ${chapter-list} 3000", + chapter_next_mbtn_right_command = "script-binding select/select-chapter; script-message-to osc osc-hide", + + audio_track_mbtn_left_command = "cycle audio", + audio_track_mbtn_mid_command = "cycle audio down", + audio_track_mbtn_right_command = "script-binding select/select-aid; script-message-to osc osc-hide", + audio_track_wheel_down_command = "cycle audio", + audio_track_wheel_up_command = "cycle audio down", + + sub_track_mbtn_left_command = "cycle sub", + sub_track_mbtn_mid_command = "cycle sub down", + sub_track_mbtn_right_command = "script-binding select/select-sid; script-message-to osc osc-hide", + sub_track_wheel_down_command = "cycle sub", + sub_track_wheel_up_command = "cycle sub down", + + volume_mbtn_left_command = "no-osd cycle mute", + volume_mbtn_mid_command = "", + volume_mbtn_right_command = "script-binding select/select-audio-device; script-message-to osc osc-hide", + volume_wheel_down_command = "add volume -5", + volume_wheel_up_command = "add volume 5", + + fullscreen_mbtn_left_command = "cycle fullscreen", + fullscreen_mbtn_mid_command = "", + fullscreen_mbtn_right_command = "cycle window-maximized", + -- luacheck: pop +} + +for i = 1, 99 do + user_opts["custom_button_" .. i .. "_content"] = "" + user_opts["custom_button_" .. i .. "_mbtn_left_command"] = "" + user_opts["custom_button_" .. i .. "_mbtn_mid_command"] = "" + user_opts["custom_button_" .. i .. "_mbtn_right_command"] = "" + user_opts["custom_button_" .. i .. "_wheel_down_command"] = "" + user_opts["custom_button_" .. i .. "_wheel_up_command"] = "" +end + +local icon_font = "mpv-osd-symbols" + +-- Running this in Lua 5.3+ or LuaJIT converts a hexadecimal Unicode code point +-- to the decimal value of every byte for Lua 5.1 and 5.2 compatibility: +-- glyph='\u{e000}' output='' +-- for i = 1, #glyph do output = output .. '\\' .. string.byte(glyph, i) end +-- print(output) +local icons = { + menu = "\238\132\130", -- E102 + prev = "\238\132\144", -- E110 + next = "\238\132\129", -- E101 + pause = "\238\128\130", -- E002 + play = "\238\132\129", -- E101 + clock = "\238\128\134", -- E006 + play_backward = "\238\132\144", -- E110 + skip_backward = "\238\128\132", -- E004 + skip_forward = "\238\128\133", -- E005 + chapter_prev = "\238\132\132", -- E104 + chapter_next = "\238\132\133", -- E105 + audio = "\238\132\134", -- E106 + subtitle = "\238\132\135", -- E107 + mute = "\238\132\138", -- E10A + volume = {"\238\132\139", "\238\132\140", "\238\132\141", "\238\132\142"},-- E10B E10C E10D E10E + fullscreen = "\238\132\136", -- E108 + exit_fullscreen = "\238\132\137",-- E109 + close = "\238\132\149", -- E115 + minimize = "\238\132\146", -- E112 + maximize = "\238\132\147", -- E113 + unmaximize = "\238\132\148", -- E114 } local osc_param = { -- calculated by osc_init() @@ -94,12 +183,13 @@ local margins_opts = { } local tick_delay = 1 / 60 -local tracks_osc = {} -local tracks_mpv = {} +local audio_track_count = 0 +local sub_track_count = 0 local window_control_box_width = 80 local layouts = {} local is_december = os.date("*t").month == 12 local UNICODE_MINUS = string.char(0xe2, 0x88, 0x92) -- UTF-8 for U+2212 MINUS SIGN +local last_custom_button = 0 local function osc_color_convert(color) return color:sub(6,7) .. color:sub(4,5) .. color:sub(2,3) @@ -111,24 +201,24 @@ local osc_styles local function set_osc_styles() osc_styles = { - bigButtons = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\3c&HFFFFFF\\fs50\\fnmpv-osd-symbols}", - smallButtonsL = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.small_buttonsL_color) .. "\\3c&HFFFFFF\\fs19\\fnmpv-osd-symbols}", + bigButtons = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\3c&HFFFFFF\\fs50\\fn" .. icon_font .. "}", + smallButtonsL = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.small_buttonsL_color) .. "\\3c&HFFFFFF\\fs19\\fn" .. icon_font .. "}", smallButtonsLlabel = "{\\fscx105\\fscy105\\fn" .. mp.get_property("options/osd-font") .. "}", - smallButtonsR = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.small_buttonsR_color) .. "\\3c&HFFFFFF\\fs30\\fnmpv-osd-symbols}", - topButtons = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.top_buttons_color) .. "\\3c&HFFFFFF\\fs12\\fnmpv-osd-symbols}", + smallButtonsR = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.small_buttonsR_color) .. "\\3c&HFFFFFF\\fs30\\fn" .. icon_font .. "}", + topButtons = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.top_buttons_color) .. "\\3c&HFFFFFF\\fs12\\fn" .. icon_font .. "}", elementDown = "{\\1c&H" .. osc_color_convert(user_opts.held_element_color) .."}", timecodes = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.timecode_color) .. "\\3c&HFFFFFF\\fs20}", - vidtitle = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.title_color) .. "\\3c&HFFFFFF\\fs12\\q2}", + vidtitle = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.title_color) .. "\\3c&HFFFFFF\\fs14\\q2}", box = "{\\rDefault\\blur0\\bord1\\1c&H" .. osc_color_convert(user_opts.background_color) .. "\\3c&HFFFFFF}", - topButtonsBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.top_buttons_color) .. "\\3c&HFFFFFF\\fs18\\fnmpv-osd-symbols}", - smallButtonsBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\3c&HFFFFFF\\fs28\\fnmpv-osd-symbols}", + topButtonsBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.top_buttons_color) .. "\\3c&HFFFFFF\\fs18\\fn" .. icon_font .. "}", + smallButtonsBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\3c&HFFFFFF\\fs28\\fn" .. icon_font .. "}", timecodesBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.timecode_color) .."\\3c&HFFFFFF\\fs27}", timePosBar = "{\\blur0\\bord".. user_opts.tooltipborder .."\\1c&H" .. osc_color_convert(user_opts.time_pos_color) .. "\\3c&H" .. osc_color_convert(user_opts.time_pos_outline_color) .. "\\fs30}", vidtitleBar = "{\\blur0\\bord0\\1c&H" .. osc_color_convert(user_opts.title_color) .. "\\3c&HFFFFFF\\fs18\\q2}", - wcButtons = "{\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\fs24\\fnmpv-osd-symbols}", + wcButtons = "{\\1c&H" .. osc_color_convert(user_opts.buttons_color) .. "\\fs24\\fn" .. icon_font .. "}", wcTitle = "{\\1c&H" .. osc_color_convert(user_opts.title_color) .. "\\fs24\\q2}", wcBar = "{\\1c&H" .. osc_color_convert(user_opts.background_color) .. "}", } @@ -138,6 +228,7 @@ end local state = { showtime = nil, -- time of last invocation (last mouse move) touchtime = nil, -- time of last invocation (last touch event) + touchpoints = {}, -- current touch points osc_visible = false, anistart = nil, -- time when the animation started anitype = nil, -- current type of animation @@ -151,9 +242,8 @@ local state = { initREQ = false, -- is a re-init request pending? marginsREQ = false, -- is a margins update pending? last_mouseX = nil, last_mouseY = nil, -- last mouse position, to detect significant mouse movement + last_touchX = -1, last_touchY = -1, -- last touch position mouse_in_window = false, - message_text = nil, - message_hide_timer = nil, fullscreen = false, tick_timer = nil, tick_last_time = 0, -- when the last tick() was run @@ -171,6 +261,11 @@ local state = { maximized = false, osd = mp.create_osd_overlay("ass-events"), chapter_list = {}, -- sorted by time + visibility_modes = {}, -- visibility_modes to cycle through + osc_message_warned = false, -- deprecation warnings + osc_chapterlist_warned = false, + osc_playlist_warned = false, + osc_tracklist_warned = false, } local logo_lines = { @@ -241,9 +336,19 @@ local function get_virt_scale_factor() return osc_param.playresx / w, osc_param.playresy / h end +local function recently_touched() + if state.touchtime == nil then + return false + end + return state.touchtime + 1 >= mp.get_time() +end + -- return mouse position in virtual ASS coordinates (playresx/y) local function get_virt_mouse_pos() - if state.mouse_in_window then + if recently_touched() then + local sx, sy = get_virt_scale_factor() + return state.last_touchX * sx, state.last_touchY * sy + elseif state.mouse_in_window then local sx, sy = get_virt_scale_factor() local x, y = mp.get_mouse_pos() return x * sx, y * sy @@ -343,13 +448,6 @@ local function get_slider_value(element) return get_slider_value_at(element, get_virt_mouse_pos()) end -local function countone(val) - if not user_opts.iamaprogrammer then - val = val + 1 - end - return val -end - -- align: -1 .. +1 -- frame: size of the containing area -- obj: size of the object that should be positioned inside the area @@ -416,6 +514,10 @@ local function get_touchtimeout() return state.touchtime + (get_hidetimeout() / 1000) - mp.get_time() end +local function cache_enabled() + return state.cache_state and #state.cache_state["seekable-ranges"] > 0 +end + local function reset_margins() if state.using_video_margins then for _, mopt in ipairs(margins_opts) do @@ -506,223 +608,22 @@ end -- --- Message display --- - --- pos is 1 based -local function limited_list(prop, pos) - local proplist = mp.get_property_native(prop, {}) - local count = #proplist - if count == 0 then - return count, proplist - end - - local fs = tonumber(mp.get_property('options/osd-font-size')) - local max = math.ceil(osc_param.unscaled_y*0.75 / fs) - if max % 2 == 0 then - max = max - 1 - end - local delta = math.ceil(max / 2) - 1 - local begi = math.max(math.min(pos - delta, count - max + 1), 1) - local endi = math.min(begi + max - 1, count) - - local reslist = {} - for i=begi, endi do - local item = proplist[i] - item.current = (i == pos) and true or nil - table.insert(reslist, item) - end - return count, reslist -end - -local function get_playlist() - local pos = mp.get_property_number('playlist-pos', 0) + 1 - local count, limlist = limited_list('playlist', pos) - if count == 0 then - return 'Empty playlist.' - end - - local message = string.format('Playlist [%d/%d]:\n', pos, count) - local show = mp.get_property_native('osd-playlist-entry') - local trailing_slash_pattern = mp.get_property("platform") == "windows" - and "[/\\]+$" or "/+$" - for _, v in ipairs(limlist) do - local entry = v.title - if not entry or show ~= 'title' then - entry = v.filename - if not entry:find("://") then - entry = select(2, utils.split_path( - entry:gsub(trailing_slash_pattern, ""))) - end - end - if v.title and show == 'both' then - entry = string.format('%s (%s)', v.title, entry) - end - message = string.format('%s %s %s\n', message, - (v.current and '●' or '○'), entry) - end - return message -end - -local function get_chapterlist() - local pos = mp.get_property_number('chapter', 0) + 1 - local count, limlist = limited_list('chapter-list', pos) - if count == 0 then - return 'No chapters.' - end - - local message = string.format('Chapters [%d/%d]:\n', pos, count) - for i, v in ipairs(limlist) do - local time = mp.format_time(v.time) - local title = v.title - if title == nil then - title = string.format('Chapter %02d', i) - end - message = string.format('%s[%s] %s %s\n', message, time, - (v.current and '●' or '○'), title) - end - return message -end - -local function render_message(ass) - if state.message_hide_timer and state.message_hide_timer:is_enabled() and - state.message_text - then - local _, lines = string.gsub(state.message_text, "\\N", "") - - local fontsize = tonumber(mp.get_property("options/osd-font-size")) - local outline = tonumber(mp.get_property("options/osd-border-size")) - local maxlines = math.ceil(osc_param.unscaled_y*0.75 / fontsize) - local counterscale = osc_param.playresy / osc_param.unscaled_y - - fontsize = fontsize * counterscale / math.max(0.65 + math.min(lines/maxlines, 1), 1) - outline = outline * counterscale / math.max(0.75 + math.min(lines/maxlines, 1)/2, 1) - - local style = "{\\bord" .. outline .. "\\fs" .. fontsize .. "}" - - - ass:new_event() - ass:append(style .. state.message_text) - else - state.message_text = nil - end -end - -local function show_message(text, duration) - --print("text: "..text.." duration: " .. duration) - if duration == nil then - duration = tonumber(mp.get_property("options/osd-duration")) / 1000 - elseif type(duration) ~= "number" then - print("duration: " .. duration) - end - - -- cut the text short, otherwise the following functions - -- may slow down massively on huge input - text = string.sub(text, 0, 4000) - - state.message_text = mp.command_native({"escape-ass", text}) - - if not state.message_hide_timer then - state.message_hide_timer = mp.add_timeout(0, request_tick) - end - state.message_hide_timer:kill() - state.message_hide_timer.timeout = duration - state.message_hide_timer:resume() - request_tick() -end - - --- -- Tracklist Management -- -local nicetypes = {video = "Video", audio = "Audio", sub = "Subtitle"} - -- updates the OSC internal playlists, should be run each time the track-layout changes local function update_tracklist() - local tracktable = mp.get_property_native("track-list", {}) - - -- by osc_id - tracks_osc.video, tracks_osc.audio, tracks_osc.sub = {}, {}, {} - -- by mpv_id - tracks_mpv.video, tracks_mpv.audio, tracks_mpv.sub = {}, {}, {} - for n = 1, #tracktable do - if tracktable[n].type ~= "unknown" then - local type = tracktable[n].type - local mpv_id = tonumber(tracktable[n].id) - - -- by osc_id - table.insert(tracks_osc[type], tracktable[n]) + audio_track_count, sub_track_count = 0, 0 - -- by mpv_id - tracks_mpv[type][mpv_id] = tracktable[n] - tracks_mpv[type][mpv_id].osc_id = #tracks_osc[type] + for _, track in pairs(mp.get_property_native("track-list")) do + if track.type == "audio" then + audio_track_count = audio_track_count + 1 + elseif track.type == "sub" then + sub_track_count = sub_track_count + 1 end end end --- return a nice list of tracks of the given type (video, audio, sub) -local function get_tracklist(type) - local message = "Available " .. nicetypes[type] .. " Tracks: " - if #tracks_osc[type] == 0 then - message = message .. "none" - else - for n = 1, #tracks_osc[type] do - local track = tracks_osc[type][n] - local lang, title, selected = "unknown", "", "○" - if track.lang ~= nil then lang = track.lang end - if track.title ~= nil then title = track.title end - if track.id == tonumber(mp.get_property(type)) then - selected = "●" - end - message = message.."\n"..selected.." "..n..": ["..lang.."] "..title - end - end - return message -end - --- relatively change the track of given <type> by <next> tracks - --(+1 -> next, -1 -> previous) -local function set_track(type, next) - local current_track_mpv, current_track_osc - if mp.get_property(type) == "no" then - current_track_osc = 0 - else - current_track_mpv = tonumber(mp.get_property(type)) - current_track_osc = tracks_mpv[type][current_track_mpv].osc_id - end - local new_track_osc = (current_track_osc + next) % (#tracks_osc[type] + 1) - local new_track_mpv - if new_track_osc == 0 then - new_track_mpv = "no" - else - new_track_mpv = tracks_osc[type][new_track_osc].id - end - - mp.commandv("set", type, new_track_mpv) - - if new_track_osc == 0 then - show_message(nicetypes[type] .. " Track: none") - else - show_message(nicetypes[type] .. " Track: " - .. new_track_osc .. "/" .. #tracks_osc[type] - .. " [".. (tracks_osc[type][new_track_osc].lang or "unknown") .."] " - .. (tracks_osc[type][new_track_osc].title or "")) - end -end - --- get the currently selected track of <type>, OSC-style counted -local function get_track(type) - local track = mp.get_property(type) - if track ~= "no" and track ~= nil then - local tr = tracks_mpv[type][tonumber(track)] - if tr then - return tr.osc_id - end - end - return 0 -end - -- WindowControl helpers local function window_controls_enabled() local val = user_opts.windowcontrols @@ -1223,7 +1124,7 @@ local function add_layout(name) return elements[name].layout else - msg.error("Can't add_layout to element \""..name.."\", doesn't exist.") + msg.error("Can't add_layout to element '"..name.."', doesn't exist.") end end @@ -1282,7 +1183,7 @@ local function window_controls(topbar) -- Close: 🗙 local ne = new_element("close", "button") - ne.content = "\238\132\149" + ne.content = icons.close ne.eventresponder["mbtn_left_up"] = function () mp.commandv("quit") end lo = add_layout("close") @@ -1291,7 +1192,7 @@ local function window_controls(topbar) -- Minimize: 🗕 ne = new_element("minimize", "button") - ne.content = "\238\132\146" + ne.content = icons.minimize ne.eventresponder["mbtn_left_up"] = function () mp.commandv("cycle", "window-minimized") end lo = add_layout("minimize") @@ -1301,9 +1202,9 @@ local function window_controls(topbar) -- Maximize: 🗖 /🗗 ne = new_element("maximize", "button") if state.maximized or state.fullscreen then - ne.content = "\238\132\148" + ne.content = icons.unmaximize else - ne.content = "\238\132\147" + ne.content = icons.maximize end ne.eventresponder["mbtn_left_up"] = function () @@ -1436,12 +1337,12 @@ layouts["box"] = function () lo.style = osc_styles.vidtitle lo.button.maxchars = user_opts.boxmaxchars - lo = add_layout("pl_prev") + lo = add_layout("playlist_prev") lo.geometry = {x = (posX - pos_offsetX), y = titlerowY, an = 7, w = 12, h = 12} lo.style = osc_styles.topButtons - lo = add_layout("pl_next") + lo = add_layout("playlist_next") lo.geometry = {x = (posX + pos_offsetX), y = titlerowY, an = 9, w = 12, h = 12} lo.style = osc_styles.topButtons @@ -1453,42 +1354,42 @@ layouts["box"] = function () local bigbtnrowY = posY - pos_offsetY + 35 local bigbtndist = 60 - lo = add_layout("playpause") + lo = add_layout("play_pause") lo.geometry = {x = posX, y = bigbtnrowY, an = 5, w = 40, h = 40} lo.style = osc_styles.bigButtons - lo = add_layout("skipback") + lo = add_layout("skip_backward") lo.geometry = {x = posX - bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40} lo.style = osc_styles.bigButtons - lo = add_layout("skipfrwd") + lo = add_layout("skip_forward") lo.geometry = {x = posX + bigbtndist, y = bigbtnrowY, an = 5, w = 40, h = 40} lo.style = osc_styles.bigButtons - lo = add_layout("ch_prev") + lo = add_layout("chapter_prev") lo.geometry = {x = posX - (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40} lo.style = osc_styles.bigButtons - lo = add_layout("ch_next") + lo = add_layout("chapter_next") lo.geometry = {x = posX + (bigbtndist * 2), y = bigbtnrowY, an = 5, w = 40, h = 40} lo.style = osc_styles.bigButtons - lo = add_layout("cy_audio") + lo = add_layout("audio_track") lo.geometry = {x = posX - pos_offsetX, y = bigbtnrowY, an = 1, w = 70, h = 18} lo.style = osc_styles.smallButtonsL - lo = add_layout("cy_sub") + lo = add_layout("sub_track") lo.geometry = {x = posX - pos_offsetX, y = bigbtnrowY, an = 7, w = 70, h = 18} lo.style = osc_styles.smallButtonsL - lo = add_layout("tog_fs") + lo = add_layout("fullscreen") lo.geometry = {x = posX+pos_offsetX - 25, y = bigbtnrowY, an = 4, w = 25, h = 25} lo.style = osc_styles.smallButtonsR @@ -1644,13 +1545,13 @@ layouts["slimbox"] = function () lo.alpha[3] = user_opts.boxalpha end -local function bar_layout(direction) +local function bar_layout(direction, slim) local osc_geo = { x = -2, y = nil, an = (direction < 0) and 7 or 1, w = nil, - h = 56, + h = slim and 25 or 56, } local padX = 9 @@ -1684,12 +1585,18 @@ local function bar_layout(direction) osc_param.playresx = osc_param.playresy * osc_param.display_aspect end - osc_geo.y = direction * (54 + user_opts.barmargin) + osc_geo.y = direction * (osc_geo.h - 2 + user_opts.barmargin) osc_geo.w = osc_param.playresx + 4 if direction < 0 then osc_geo.y = osc_geo.y + osc_param.playresy end + if direction < 0 then + osc_param.video_margins.b = osc_geo.h / osc_param.playresy + else + osc_param.video_margins.t = osc_geo.h / osc_param.playresy + end + local line1 = osc_geo.y - direction * (9 + padY) local line2 = osc_geo.y - direction * (36 + padY) @@ -1723,28 +1630,54 @@ local function bar_layout(direction) lo.alpha[1] = user_opts.boxalpha + -- Menu + geo = { x = osc_geo.x + padX + 4, y = line1, an = 4, w = 18, h = 18 - padY } + lo = add_layout("menu") + lo.geometry = geo + lo.style = osc_styles.topButtonsBar + -- Playlist prev/next - geo = { x = osc_geo.x + padX, y = line1, - an = 4, w = 18, h = 18 - padY } - lo = add_layout("pl_prev") + geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h } + lo = add_layout("playlist_prev") lo.geometry = geo lo.style = osc_styles.topButtonsBar geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h } - lo = add_layout("pl_next") + lo = add_layout("playlist_next") lo.geometry = geo lo.style = osc_styles.topButtonsBar local t_l = geo.x + geo.w + padX - -- Cache - geo = { x = osc_geo.x + osc_geo.w - padX, y = geo.y, - an = 6, w = 150, h = geo.h } - lo = add_layout("cache") - lo.geometry = geo - lo.style = osc_styles.vidtitleBar + -- Custom buttons + local t_r = osc_geo.x + osc_geo.w + + for i = last_custom_button, 1, -1 do + t_r = t_r - padX + geo = { x = t_r, y = geo.y, an = 6, w = geo.w, h = geo.h } + t_r = t_r - geo.w + lo = add_layout("custom_button_" .. i) + lo.geometry = geo + lo.style = osc_styles.vidtitleBar + end - local t_r = geo.x - geo.w - padX*2 + t_r = t_r - padX + + if slim then + -- Fullscreen button + geo = { x = t_r, y = geo.y, an = 6, w = buttonW, h = geo.h } + lo = add_layout("fullscreen") + lo.geometry = geo + lo.style = osc_styles.topButtonsBar + else + -- Cache + geo = { x = t_r, y = geo.y, an = 6, w = 150, h = geo.h } + lo = add_layout("cache") + lo.geometry = geo + lo.style = osc_styles.vidtitleBar + end + + t_r = t_r - geo.w - padX -- Title geo = { x = t_l, y = geo.y, an = 4, @@ -1755,21 +1688,24 @@ local function bar_layout(direction) osc_styles.vidtitleBar, geo.x, geo.y-geo.h, geo.w, geo.y+geo.h) + if slim then + return + end -- Playback control buttons geo = { x = osc_geo.x + padX + padwc_l, y = line2, an = 4, w = buttonW, h = 36 - padY*2} - lo = add_layout("playpause") + lo = add_layout("play_pause") lo.geometry = geo lo.style = osc_styles.smallButtonsBar geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h } - lo = add_layout("ch_prev") + lo = add_layout("chapter_prev") lo.geometry = geo lo.style = osc_styles.smallButtonsBar geo = { x = geo.x + geo.w + padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h } - lo = add_layout("ch_next") + lo = add_layout("chapter_next") lo.geometry = geo lo.style = osc_styles.smallButtonsBar @@ -1785,7 +1721,7 @@ local function bar_layout(direction) -- Fullscreen button geo = { x = osc_geo.x + osc_geo.w - buttonW - padX - padwc_r, y = geo.y, an = 4, w = buttonW, h = geo.h } - lo = add_layout("tog_fs") + lo = add_layout("fullscreen") lo.geometry = geo lo.style = osc_styles.smallButtonsBar @@ -1797,12 +1733,12 @@ local function bar_layout(direction) -- Track selection buttons geo = { x = geo.x - tsW - padX, y = geo.y, an = geo.an, w = tsW, h = geo.h } - lo = add_layout("cy_sub") + lo = add_layout("sub_track") lo.geometry = geo lo.style = osc_styles.smallButtonsBar geo = { x = geo.x - geo.w - padX, y = geo.y, an = geo.an, w = geo.w, h = geo.h } - lo = add_layout("cy_audio") + lo = add_layout("audio_track") lo.geometry = geo lo.style = osc_styles.smallButtonsBar @@ -1842,12 +1778,6 @@ local function bar_layout(direction) lo.slider.tooltip_an = 5 lo.slider.stype = user_opts["seekbarstyle"] lo.slider.rtype = user_opts["seekrangestyle"] - - if direction < 0 then - osc_param.video_margins.b = osc_geo.h / osc_param.playresy - else - osc_param.video_margins.t = osc_geo.h / osc_param.playresy - end end layouts["bottombar"] = function() @@ -1858,6 +1788,38 @@ layouts["topbar"] = function() bar_layout(1) end +layouts["slimbottombar"] = function() + bar_layout(-1, true) +end + +layouts["slimtopbar"] = function() + bar_layout(1, true) +end + + +local function bind_mouse_buttons(element_name) + for _, button in pairs({"mbtn_left", "mbtn_mid", "mbtn_right"}) do + local command = user_opts[element_name .. "_" .. button .. "_command"] + + if command ~= "" then + elements[element_name].eventresponder[button .. "_up"] = function () + mp.command(command) + end + end + end + + if user_opts.scrollcontrols then + for _, button in pairs({"wheel_down", "wheel_up"}) do + local command = user_opts[element_name .. "_" .. button .. "_command"] + + if command and command ~= "" then + elements[element_name].eventresponder[button .. "_press"] = function () + mp.command(command) + end + end + end + end +end local function osc_init() msg.debug("osc_init") @@ -1916,204 +1878,124 @@ local function osc_init() title = title:gsub("\n", " ") return title ~= "" and mp.command_native({"escape-ass", title}) or "mpv" end + bind_mouse_buttons("title") - ne.eventresponder["mbtn_left_up"] = function () - local title = mp.get_property_osd("media-title") - if have_pl then - title = string.format("[%d/%d] %s", countone(pl_pos - 1), - pl_count, title) - end - show_message(title) - end - - ne.eventresponder["mbtn_right_up"] = - function () show_message(mp.get_property_osd("filename")) end + -- menu + ne = new_element("menu", "button") + ne.content = icons.menu + bind_mouse_buttons("menu") -- playlist buttons -- prev - ne = new_element("pl_prev", "button") + ne = new_element("playlist_prev", "button") - ne.content = "\238\132\144" + ne.content = icons.prev ne.enabled = (pl_pos > 1) or (loop ~= "no") - ne.eventresponder["mbtn_left_up"] = - function () - mp.commandv("playlist-prev", "weak") - if user_opts.playlist_osd then - show_message(get_playlist(), 3) - end - end - ne.eventresponder["shift+mbtn_left_up"] = - function () show_message(get_playlist(), 3) end - ne.eventresponder["mbtn_right_up"] = - function () show_message(get_playlist(), 3) end + bind_mouse_buttons("playlist_prev") --next - ne = new_element("pl_next", "button") + ne = new_element("playlist_next", "button") - ne.content = "\238\132\129" + ne.content = icons.next ne.enabled = (have_pl and (pl_pos < pl_count)) or (loop ~= "no") - ne.eventresponder["mbtn_left_up"] = - function () - mp.commandv("playlist-next", "weak") - if user_opts.playlist_osd then - show_message(get_playlist(), 3) - end - end - ne.eventresponder["shift+mbtn_left_up"] = - function () show_message(get_playlist(), 3) end - ne.eventresponder["mbtn_right_up"] = - function () show_message(get_playlist(), 3) end + bind_mouse_buttons("playlist_next") -- big buttons - --playpause - ne = new_element("playpause", "button") + --play_pause + ne = new_element("play_pause", "button") ne.content = function () - if mp.get_property("pause") == "yes" then - if mp.get_property("play-direction", "forward") ~= "backward" then - return ("\238\132\129") - else - return ("\238\132\144") - end - else - return ("\238\128\130") + if mp.get_property_bool("paused-for-cache") ~= false then + return icons.clock end + + if not mp.get_property_native("pause") then + return icons.pause + end + + return mp.get_property("play-direction") == "forward" + and icons.play + or icons.play_backward end - ne.eventresponder["mbtn_left_up"] = - function () mp.commandv("cycle", "pause") end + bind_mouse_buttons("play_pause") - --skipback - ne = new_element("skipback", "button") + --skip_backward + ne = new_element("skip_backward", "button") ne.softrepeat = true - ne.content = "\238\128\132" + ne.content = icons.skip_backward ne.eventresponder["mbtn_left_down"] = function () mp.commandv("seek", -5) end - ne.eventresponder["shift+mbtn_left_down"] = + ne.eventresponder["mbtn_mid"] = function () mp.commandv("frame-back-step") end ne.eventresponder["mbtn_right_down"] = function () mp.commandv("seek", -30) end - --skipfrwd - ne = new_element("skipfrwd", "button") + --skip_forward + ne = new_element("skip_forward", "button") ne.softrepeat = true - ne.content = "\238\128\133" + ne.content = icons.skip_forward ne.eventresponder["mbtn_left_down"] = function () mp.commandv("seek", 10) end - ne.eventresponder["shift+mbtn_left_down"] = + ne.eventresponder["mbtn_mid"] = function () mp.commandv("frame-step") end ne.eventresponder["mbtn_right_down"] = function () mp.commandv("seek", 60) end - --ch_prev - ne = new_element("ch_prev", "button") + --chapter_prev + ne = new_element("chapter_prev", "button") ne.enabled = have_ch - ne.content = "\238\132\132" - ne.eventresponder["mbtn_left_up"] = - function () - mp.commandv("add", "chapter", -1) - if user_opts.chapters_osd then - show_message(get_chapterlist(), 3) - end - end - ne.eventresponder["shift+mbtn_left_up"] = - function () show_message(get_chapterlist(), 3) end - ne.eventresponder["mbtn_right_up"] = - function () show_message(get_chapterlist(), 3) end + ne.content = icons.chapter_prev + bind_mouse_buttons("chapter_prev") - --ch_next - ne = new_element("ch_next", "button") + --chapter_next + ne = new_element("chapter_next", "button") ne.enabled = have_ch - ne.content = "\238\132\133" - ne.eventresponder["mbtn_left_up"] = - function () - mp.commandv("add", "chapter", 1) - if user_opts.chapters_osd then - show_message(get_chapterlist(), 3) - end - end - ne.eventresponder["shift+mbtn_left_up"] = - function () show_message(get_chapterlist(), 3) end - ne.eventresponder["mbtn_right_up"] = - function () show_message(get_chapterlist(), 3) end + ne.content = icons.chapter_next + bind_mouse_buttons("chapter_next") -- update_tracklist() - --cy_audio - ne = new_element("cy_audio", "button") + --audio_track + ne = new_element("audio_track", "button") - ne.enabled = (#tracks_osc.audio > 0) + ne.enabled = audio_track_count > 0 ne.content = function () - local aid = "–" - if get_track("audio") ~= 0 then - aid = get_track("audio") - end - return ("\238\132\134" .. osc_styles.smallButtonsLlabel - .. " " .. aid .. "/" .. #tracks_osc.audio) - end - ne.eventresponder["mbtn_left_up"] = - function () set_track("audio", 1) end - ne.eventresponder["mbtn_right_up"] = - function () set_track("audio", -1) end - ne.eventresponder["shift+mbtn_left_down"] = - function () show_message(get_tracklist("audio"), 2) end - - if user_opts.scrollcontrols then - ne.eventresponder["wheel_down_press"] = - function () set_track("audio", 1) end - ne.eventresponder["wheel_up_press"] = - function () set_track("audio", -1) end + return icons.audio .. osc_styles.smallButtonsLlabel .. " " .. + mp.get_property_number("aid", "-") .. "/" .. audio_track_count end + bind_mouse_buttons("audio_track") - --cy_sub - ne = new_element("cy_sub", "button") + --sub_track + ne = new_element("sub_track", "button") - ne.enabled = (#tracks_osc.sub > 0) + ne.enabled = sub_track_count > 0 ne.content = function () - local sid = "–" - if get_track("sub") ~= 0 then - sid = get_track("sub") - end - return ("\238\132\135" .. osc_styles.smallButtonsLlabel - .. " " .. sid .. "/" .. #tracks_osc.sub) - end - ne.eventresponder["mbtn_left_up"] = - function () set_track("sub", 1) end - ne.eventresponder["mbtn_right_up"] = - function () set_track("sub", -1) end - ne.eventresponder["shift+mbtn_left_down"] = - function () show_message(get_tracklist("sub"), 2) end - - if user_opts.scrollcontrols then - ne.eventresponder["wheel_down_press"] = - function () set_track("sub", 1) end - ne.eventresponder["wheel_up_press"] = - function () set_track("sub", -1) end + return icons.subtitle .. osc_styles.smallButtonsLlabel .. " " .. + mp.get_property_number("sid", "-") .. "/" .. sub_track_count end + bind_mouse_buttons("sub_track") - --tog_fs - ne = new_element("tog_fs", "button") + --fullscreen + ne = new_element("fullscreen", "button") ne.content = function () - if state.fullscreen then - return ("\238\132\137") - else - return ("\238\132\136") - end + return state.fullscreen and icons.exit_fullscreen or icons.fullscreen end - ne.eventresponder["mbtn_left_up"] = - function () mp.commandv("cycle", "fullscreen") end + bind_mouse_buttons("fullscreen") --seekbar ne = new_element("seekbar", "slider") ne.enabled = mp.get_property("percent-pos") ~= nil + and user_opts.layout ~= "slimbottombar" + and user_opts.layout ~= "slimtopbar" state.slider_element = ne.enabled and ne or nil -- used for forced_title ne.slider.markerF = function () local duration = mp.get_property_number("duration") @@ -2142,23 +2024,15 @@ local function osc_init() end end ne.slider.seekRangesF = function() - if user_opts.seekrangestyle == "none" then - return nil - end - local cache_state = state.cache_state - if not cache_state then + if user_opts.seekrangestyle == "none" or not cache_enabled() then return nil end local duration = mp.get_property_number("duration") if duration == nil or duration <= 0 then return nil end - local ranges = cache_state["seekable-ranges"] - if #ranges == 0 then - return nil - end local nranges = {} - for _, range in pairs(ranges) do + for _, range in pairs(state.cache_state["seekable-ranges"]) do nranges[#nranges + 1] = { ["start"] = 100 * range["start"] / duration, ["end"] = 100 * range["end"] / duration, @@ -2168,6 +2042,10 @@ local function osc_init() end ne.eventresponder["mouse_move"] = --keyframe seeking when mouse is dragged function (element) + if not element.state.mbtn_left then + return + end + -- mouse move events may pile up during seeking and may still get -- sent when the user is done seeking, so we need to throw away -- identical seeks @@ -2183,9 +2061,29 @@ local function osc_init() end end - ne.eventresponder["mbtn_left_down"] = --exact seeks on single clicks - function (element) mp.commandv("seek", get_slider_value(element), - "absolute-percent+exact") end + ne.eventresponder["mbtn_left_down"] = function (element) + element.state.mbtn_left = true + mp.commandv("seek", get_slider_value(element), "absolute-percent+exact") + end + ne.eventresponder["mbtn_left_up"] = function (element) + element.state.mbtn_left = false + end + ne.eventresponder["mbtn_right_up"] = function (element) + local chapter + local pos = get_slider_value(element) + local diff = math.huge + + for i, marker in ipairs(element.slider.markerF()) do + if math.abs(pos - marker) < diff then + diff = math.abs(pos - marker) + chapter = i + end + end + + if chapter then + mp.set_property("chapter", chapter - 1) + end + end ne.eventresponder["reset"] = function (element) element.state.lastseek = nil end @@ -2241,13 +2139,10 @@ local function osc_init() ne = new_element("cache", "button") ne.content = function () - local cache_state = state.cache_state - if not (cache_state and cache_state["seekable-ranges"] and - #cache_state["seekable-ranges"] > 0) then - -- probably not a network stream + if not cache_enabled() then return "" end - local dmx_cache = cache_state and cache_state["cache-duration"] + local dmx_cache = state.cache_state["cache-duration"] local thresh = math.min(state.dmx_cache * 0.05, 5) -- 5% or 5s if dmx_cache and math.abs(dmx_cache - state.dmx_cache) >= thresh then state.dmx_cache = dmx_cache @@ -2265,24 +2160,26 @@ local function osc_init() ne = new_element("volume", "button") ne.content = function() - local volume = mp.get_property_number("volume", 0) - local mute = mp.get_property_native("mute") - local volicon = {"\238\132\139", "\238\132\140", - "\238\132\141", "\238\132\142"} - if volume == 0 or mute then - return "\238\132\138" - else - return volicon[math.min(4,math.ceil(volume / (100/3)))] + local volume = mp.get_property_number("volume") + if volume == 0 or mp.get_property_native("mute") then + return icons.mute end + + return icons.volume[math.min(4, math.ceil(volume / (100/3)))] end - ne.eventresponder["mbtn_left_up"] = - function () mp.commandv("cycle", "mute") end + bind_mouse_buttons("volume") - if user_opts.scrollcontrols then - ne.eventresponder["wheel_up_press"] = - function () mp.commandv("osd-auto", "add", "volume", 5) end - ne.eventresponder["wheel_down_press"] = - function () mp.commandv("osd-auto", "add", "volume", -5) end + + -- custom buttons + for i = 1, math.huge do + local content = user_opts["custom_button_" .. i .. "_content"] + if not content or content == "" then + break + end + ne = new_element("custom_button_" .. i, "button") + ne.content = content + bind_mouse_buttons("custom_button_" .. i) + last_custom_button = i end @@ -2317,9 +2214,15 @@ local function show_osc() --remember last time of invocation (mouse move) state.showtime = mp.get_time() - osc_visible(true) - - if user_opts.fadeduration > 0 then + if user_opts.fadeduration <= 0 then + osc_visible(true) + elseif user_opts.fadein then + if not state.osc_visible then + state.anitype = "in" + request_tick() + end + else + osc_visible(true) state.anitype = nil end end @@ -2387,9 +2290,17 @@ local function mouse_leave() export_uistate() end -local function handle_touch() - --remember last time of invocation (touch event) - state.touchtime = mp.get_time() +local function handle_touch(_, touchpoints) + --remember last touch points + if touchpoints then + state.touchpoints = touchpoints + if #touchpoints > 0 then + --remember last time of invocation (touch event) + state.touchtime = mp.get_time() + state.last_touchX = touchpoints[1].x + state.last_touchY = touchpoints[1].y + end + end end @@ -2630,7 +2541,7 @@ local function render() if state.osc_visible then mp.enable_key_bindings("window-controls-title", "allow-vo-dragging") else - mp.disable_key_bindings("window-controls-title", "allow-vo-dragging") + mp.disable_key_bindings("window-controls-title") end state.windowcontrols_title = state.osc_visible end @@ -2665,9 +2576,6 @@ local function render() -- actual rendering local ass = assdraw.ass_new() - -- Messages - render_message(ass) - -- actual OSC if state.osc_visible then render_elements(ass) @@ -2813,12 +2721,14 @@ end -- the modes only affect internal variables and not stored on its own. local function visibility_mode(mode, no_osd) if mode == "cycle" then - if not state.enabled then - mode = "auto" - elseif user_opts.visibility ~= "always" then - mode = "always" - else - mode = "never" + for i, allowed_mode in ipairs(state.visibility_modes) do + if i == #state.visibility_modes then + mode = state.visibility_modes[1] + break + elseif user_opts.visibility == allowed_mode then + mode = state.visibility_modes[i + 1] + break + end end end @@ -2880,15 +2790,15 @@ end -- Validate string type user options local function validate_user_opts() if layouts[user_opts.layout] == nil then - msg.warn("Invalid setting \""..user_opts.layout.."\" for layout") + msg.warn("Invalid setting '"..user_opts.layout.."' for layout") user_opts.layout = "bottombar" end if user_opts.seekbarstyle ~= "bar" and user_opts.seekbarstyle ~= "diamond" and user_opts.seekbarstyle ~= "knob" then - msg.warn("Invalid setting \"" .. user_opts.seekbarstyle - .. "\" for seekbarstyle") + msg.warn("Invalid setting '" .. user_opts.seekbarstyle + .. "' for seekbarstyle") user_opts.seekbarstyle = "bar" end @@ -2897,29 +2807,29 @@ local function validate_user_opts() user_opts.seekrangestyle ~= "slider" and user_opts.seekrangestyle ~= "inverted" and user_opts.seekrangestyle ~= "none" then - msg.warn("Invalid setting \"" .. user_opts.seekrangestyle - .. "\" for seekrangestyle") + msg.warn("Invalid setting '" .. user_opts.seekrangestyle + .. "' for seekrangestyle") user_opts.seekrangestyle = "inverted" end if user_opts.seekrangestyle == "slider" and user_opts.seekbarstyle == "bar" then msg.warn( - "Using \"slider\" seekrangestyle together with \"bar\" seekbarstyle is not supported") + "Using 'slider' seekrangestyle together with 'bar' seekbarstyle is not supported") user_opts.seekrangestyle = "inverted" end if user_opts.windowcontrols ~= "auto" and user_opts.windowcontrols ~= "yes" and user_opts.windowcontrols ~= "no" then - msg.warn("windowcontrols cannot be \"" .. - user_opts.windowcontrols .. "\". Ignoring.") + msg.warn("windowcontrols cannot be '" .. + user_opts.windowcontrols .. "'. Ignoring.") user_opts.windowcontrols = "auto" end if user_opts.windowcontrols_alignment ~= "right" and user_opts.windowcontrols_alignment ~= "left" then - msg.warn("windowcontrols_alignment cannot be \"" .. - user_opts.windowcontrols_alignment .. "\". Ignoring.") + msg.warn("windowcontrols_alignment cannot be '" .. + user_opts.windowcontrols_alignment .. "'. Ignoring.") user_opts.windowcontrols_alignment = "right" end @@ -2935,13 +2845,22 @@ local function validate_user_opts() msg.warn("'" .. color .. "' is not a valid color") end end + + for str in string.gmatch(user_opts.visibility_modes, "([^_]+)") do + if str ~= "auto" and str ~= "always" and str ~= "never" then + msg.warn("Ignoring unknown visibility mode '" .. str .."' in list") + else + table.insert(state.visibility_modes, str) + end + end end local function load_plugin() mp.register_event("shutdown", shutdown) mp.register_event("start-file", request_init) mp.observe_property("track-list", "native", request_init) -mp.observe_property("playlist", "native", request_init) +mp.observe_property("playlist-count", "native", request_init) +mp.observe_property("playlist-pos", "native", request_init) mp.observe_property("chapter-list", "native", function(_, list) list = list or {} -- safety, shouldn't return nil table.sort(list, function(a, b) return a.time < b.time end) @@ -2950,19 +2869,38 @@ mp.observe_property("chapter-list", "native", function(_, list) request_init() end) -mp.register_script_message("osc-message", show_message) +-- These are for backwards compatibility only. +mp.register_script_message("osc-message", function(message, dur) + if not state.osc_message_warned then + mp.msg.warn("osc-message is deprecated and may be removed in the future.", + "Use the show-text command instead.") + state.osc_message_warned = true + end + mp.osd_message(message, dur) +end) mp.register_script_message("osc-chapterlist", function(dur) - show_message(get_chapterlist(), dur) + if not state.osc_chapterlist_warned then + mp.msg.warn("osc-chapterlist is deprecated and may be removed in the future.", + "Use show-text ${chapter-list} instead.") + state.osc_chapterlist_warned = true + end + mp.command("show-text ${chapter-list} " .. (dur and dur * 1000 or "")) end) mp.register_script_message("osc-playlist", function(dur) - show_message(get_playlist(), dur) + if not state.osc_playlist_warned then + mp.msg.warn("osc-playlist is deprecated and may be removed in the future.", + "Use show-text ${playlist} instead.") + state.osc_playlist_warned = true + end + mp.command("show-text ${playlist} " .. (dur and dur * 1000 or "")) end) mp.register_script_message("osc-tracklist", function(dur) - local message = {} - for k in pairs(nicetypes) do - table.insert(message, get_tracklist(k)) + if not state.osc_tracklist_warned then + mp.msg.warn("osc-tracklist is deprecated and may be removed in the future.", + "Use show-text ${track-list} instead.") + state.osc_tracklist_warned = true end - show_message(table.concat(message, '\n\n'), dur) + mp.command("show-text ${track-list} " .. (dur and dur * 1000 or "")) end) mp.observe_property("fullscreen", "bool", function(_, val) @@ -2989,6 +2927,8 @@ end) mp.observe_property("display-fps", "number", set_tick_delay) mp.observe_property("pause", "bool", pause_state) +mp.observe_property("volume", "number", request_tick) +mp.observe_property("mute", "bool", request_tick) mp.observe_property("demuxer-cache-state", "native", cache_state) mp.observe_property("vo-configured", "bool", request_tick) mp.observe_property("playback-time", "number", request_tick) @@ -3015,13 +2955,13 @@ do_enable_keybindings() mp.set_key_bindings({ {"mbtn_left", function() process_event("mbtn_left", "up") end, function() process_event("mbtn_left", "down") end}, - {"shift+mbtn_left", function() process_event("shift+mbtn_left", "up") end, - function() process_event("shift+mbtn_left", "down") end}, + {"mbtn_mid", function() process_event("mbtn_mid", "up") end, + function() process_event("mbtn_mid", "down") end}, {"mbtn_right", function() process_event("mbtn_right", "up") end, function() process_event("mbtn_right", "down") end}, - -- alias to shift_mbtn_left for single-handed mouse use - {"mbtn_mid", function() process_event("shift+mbtn_left", "up") end, - function() process_event("shift+mbtn_left", "down") end}, + -- alias shift+mbtn_left to mbtn_mid for touchpads + {"shift+mbtn_left", function() process_event("mbtn_mid", "up") end, + function() process_event("mbtn_mid", "down") end}, {"wheel_up", function() process_event("wheel_up", "press") end}, {"wheel_down", function() process_event("wheel_down", "press") end}, {"mbtn_left_dbl", "ignore"}, @@ -3038,6 +2978,11 @@ mp.enable_key_bindings("window-controls") mp.register_script_message("osc-visibility", visibility_mode) mp.register_script_message("osc-show", show_osc) +mp.register_script_message("osc-hide", function () + if user_opts.visibility == "auto" then + osc_visible(false) + end +end) mp.add_key_binding(nil, "visibility", function() visibility_mode("cycle") end) mp.register_script_message("osc-idlescreen", idlescreen_visibility) |
