summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE.GPL9
-rw-r--r--LICENSE.LGPL27
-rw-r--r--README3
-rw-r--r--osc-mod.lua921
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!
diff --git a/README b/README
index c189c5e..30e2355 100644
--- a/README
+++ b/README
@@ -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)