From 7b84b40372b3e3639c46de5a63113eed5732b66f Mon Sep 17 00:00:00 2001 From: NRK Date: Sat, 7 Mar 2026 14:02:11 +0000 Subject: [PATCH] osc: define and add support for "preview" api this adds a "standard" api for ui scripts and thumbnailers to communicate with each other, based on the simple thumbfast api [0]. the api works as follows: * To issue a thumbnail draw command, the UI script will set the property `user-data/osc/draw-preview`. * To clear the thumbnail, the UI script will set the previously mentioned property to `nil`. a more ideal api would make it so that the thumbnailer script only generates the thumbnail and doesn't need to draw at all. but such api is vastly more complex [4] and would require a lot more work and maintenance on mpv's side. this is a decent enough api that allows arbitrary thumbnailers and ui scripts to communicate between each other and work together, while being simple enough that it can be supported without too much maintenance. this change has been tested with work with thumbfast [1]. and for demonstration that this api can be useful outside of osc, it has also been tested to work on mfpbar's thumbnailer branch [2]. the code to determine thumbnail x,y is based on the osc fork inside of thumbfast [3]. [0]: https://github.com/po5/thumbfast?tab=readme-ov-file#for-ui-developers-how-to-add-thumbfast-support-to-your-script [1]: https://github.com/po5/thumbfast/pull/173 [2]: https://codeberg.org/NRK/mpv-toolbox/src/branch/thumbnailer/mfpbar [3]: https://github.com/po5/thumbfast/tree/vanilla-osc [4]: https://github.com/mpv-player/mpv/discussions/17654 --- DOCS/man/input.rst | 4 ++++ DOCS/man/osc.rst | 38 ++++++++++++++++++++++++++++++++ player/lua/osc.lua | 54 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/DOCS/man/input.rst b/DOCS/man/input.rst index ea61a92d93cce..f043580c8efe5 100644 --- a/DOCS/man/input.rst +++ b/DOCS/man/input.rst @@ -3924,6 +3924,10 @@ Property list the left, right, top, and bottom margins respectively. Values are between 0.0 and 1.0, normalized to window width/height. + ``user-data/osc/draw-preview`` + Used to communicate between osc and compatible thumbnailer (if + installed). See `OSC Preview API`_ section for more details. + ``user-data/mpv/ytdl`` Data shared by the builtin ytdl hook script. diff --git a/DOCS/man/osc.rst b/DOCS/man/osc.rst index e261e40206359..63bb543a00f78 100644 --- a/DOCS/man/osc.rst +++ b/DOCS/man/osc.rst @@ -586,6 +586,12 @@ Configurable Options Use display fps to calculate the interval between OSC redraws. +``max_thumb_size`` + Default: 200 + + Maximum display size of the preview thumbnail. Only meaningful when a + Thumbnailer is active. See the `OSC Preview API`_ section. + The following options configure what commands are run when the buttons are clicked. ``mbtn_mid`` commands are also triggered with ``shift+mbtn_left``. @@ -719,3 +725,35 @@ to set auto mode (the default) with ``b``:: Controls the visibility of the mpv logo on idle. Valid arguments are ``yes``, ``no``, and ``cycle`` to toggle between yes and no. If a second argument is passed (any value), then the output on the OSD will be silenced. + +OSC Preview API +~~~~~~~~~~~~~~~ + +The OSC supports displaying preview thumbnails when hovering over the seekbar if +a compatible thumbnailer script is installed. It communicates with a thumbnailer +script via the following ``user-data`` properties: + +``user-data/osc/draw-preview`` + Set by the OSC to request a thumbnail within the currently playing file. + The requested thumbnail timestamp in seconds is set in + ``user-data/osc/hover-sec``. The ``draw-preview`` property is a table with + the following fields: + + ``x``, ``y`` + Top-left coordinates (positive integers) to draw the thumbnail at. + + ``w``, ``h`` + Width and height (positive non-zero integers) of the area to draw the + thumbnail in. Note that this only specifies the drawing width and + height, the actual backing thumbnail size may differ. + + ``ass`` + Optional ASS string to render alongside the thumbnail, using OSD + coordinates. This can be used for decorations such as a thumbnail + border. + + The OSC sets this property to ``nil`` to signal the thumbnailer to clear + the displayed thumbnail. + +Avoid installing/enabling multiple thumbnailer script since this api expects +only one to be active at a time. diff --git a/player/lua/osc.lua b/player/lua/osc.lua index 3de5264063661..e716bbe259a3c 100644 --- a/player/lua/osc.lua +++ b/player/lua/osc.lua @@ -79,6 +79,8 @@ local user_opts = { tick_delay = 1 / 60, -- minimum interval between OSC redraws in seconds tick_delay_follow_display_fps = false, -- use display fps as the minimum interval + max_thumb_size = 200, -- maximum display size of preview thumbnails + -- luacheck: push ignore -- luacheck: max line length menu_mbtn_left_command = "script-binding select/menu; script-message-to osc osc-hide", @@ -1177,6 +1179,56 @@ local function render_elements(master_ass) ass_append_alpha(elem_ass, slider_lo.alpha, 0) elem_ass:append(tooltiplabel) + local hover_sec = mp.get_property_number("duration", 0) * (sliderpos / 100) + mp.set_property_number("user-data/osc/hover-sec", hover_sec) + + -- thumbnail + local osd_w, osd_h = mp.get_osd_size() + local vop = mp.get_property_native("video-out-params") + local draw_thumbnail = osd_w > 0 and vop + if draw_thumbnail then + local r_w, r_h = get_virt_scale_factor() + local thumb_max = math.min(user_opts.max_thumb_size, + math.min(osd_w, osd_h) * 0.25) + local scale = thumb_max / math.max(vop.dw, vop.dh) + local thumb_w = math.floor(vop.dw * scale + 0.5) + local thumb_h = math.floor(vop.dh * scale + 0.5) + local tooltip_font_size = (user_opts.layout == "box" or + user_opts.layout == "slimbox") and 2 or 12 + local thumb_tx = tx + local thumb_ty = user_opts.layout ~= "topbar" and element.hitbox.y1 - 8 or + element.hitbox.y2 + tooltip_font_size + 8 + local thumb_pad = 4 + local thumb_margin_x = 20 / r_w + local thumb_margin_y = (4 + user_opts.tooltipborder) / r_h + thumb_pad + local thumb_x = math.min(osd_w - thumb_w - thumb_margin_x, + math.max(thumb_margin_x, thumb_tx / r_w - thumb_w / 2)) + local thumb_y = thumb_ty / r_h + (user_opts.layout ~= "topbar" and + -(thumb_h + tooltip_font_size / r_h + thumb_margin_y) or + thumb_margin_y) + + local thumb_req = { + x = math.floor(thumb_x + 0.5), y = math.floor(thumb_y + 0.5), + w = math.floor(thumb_w + 0.5), h = math.floor(thumb_h + 0.5), + } + + local thumb_ass = assdraw.ass_new() + thumb_ass:new_event() + thumb_ass:pos(thumb_req.x, thumb_req.y) + thumb_ass:an(7) + thumb_ass:append(osc_styles.timePosBar) + thumb_ass:append("{\\1a&H20&}") + thumb_ass:draw_start() + thumb_ass:rect_cw(-thumb_pad, -thumb_pad, + thumb_req.w + thumb_pad, thumb_req.h + thumb_pad) + thumb_ass:draw_stop() + thumb_req.ass = thumb_ass.text + + mp.set_property_native("user-data/osc/draw-preview", thumb_req) + end + else + mp.set_property_native("user-data/osc/hover-sec", nil) + mp.set_property_native("user-data/osc/draw-preview", nil) end end @@ -3187,6 +3239,8 @@ mp.register_event("file-loaded", function() end) mp.add_hook("on_unload", 50, function() state.file_loaded = false + mp.set_property_native("user-data/osc/hover-sec", nil) + mp.set_property_native("user-data/osc/draw-preview", nil) request_tick() end)