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
This commit is contained in:
NRK authored and Kacper Michajłow committed 2026-04-17 20:36:48 +02:00
1 parent 8da99f778f
commit 09d075f475
3 files changed
+96

No files matched your search

+4
View File
@@ -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.
+38
View File
@@ -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.
+54
View File
@@ -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)