Add thumbfast compatibility layer to support alternate OSCs.

This commit is contained in:
Ian Walton 2023-02-25 18:06:55 -05:00
parent 6710f2deb8
commit acbc3e9d0c
8 changed files with 153 additions and 93 deletions

View File

@ -3,4 +3,5 @@ recursive-include jellyfin_mpv_shim/integration *
recursive-include jellyfin_mpv_shim/default_shader_pack *
recursive-include jellyfin_mpv_shim/messages *.mo
include jellyfin_mpv_shim/mouse.lua
include jellyfin_mpv_shim/trickplay.lua
include jellyfin_mpv_shim/trickplay-osc.lua
include jellyfin_mpv_shim/thumbfast.lua

View File

@ -313,12 +313,12 @@ MPV will automatically display thumbnail previews. By default it uses the Jellyf
but it can also use JellyScrub as the source. Please note that this feature will download and
uncompress all of the chapter images before it becomes available for a video. For a 4 hour movie this
causes disk usage of about 250 MB, but for the average TV episode it is around 40 MB. It also requires
overriding the default MPV OSC, which may conflict with some custom user script. The `trickplay.lua`
file contains the feature if you want to make a modified version.
overriding the default MPV OSC, which may conflict with some custom user script. Trickplay is compatible
with any OSC that uses [thumbfast](https://github.com/po5/thumbfast), as I have added a [compatibility layer](https://github.com/jellyfin/jellyfin-mpv-shim/blob/master/jellyfin_mpv_shim/thumbfast.lua).
- `thumbnail_enable` - Enable thumbnail feature. (Default: `true`)
- `thumbnail_jellyscrub` - Use JellyScrub as the thumbnail source instead of chapter images. (Default: `false`)
- `thumbnail_custom_script` - If enabled, it disables the default `trickplay.lua` so you can provide a modified version. (Default: `null`)
- `thumbnail_osc_builtin` - Disable this setting if you want to use your own custom osc but leave trickplay enabled. (Default: `true`)
- `thumbnail_preferred_size` - The ideal size for thumbnails. (Default: `320`)
### SVP Integration

View File

@ -1,7 +1,7 @@
@echo off
rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD%
pyinstaller -w --add-binary "mpv-2.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\trickplay.lua;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
pyinstaller -w --add-binary "mpv-2.dll;." --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\trickplay-osc.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\thumbfast.lua;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
if %errorlevel% neq 0 exit /b %errorlevel%
del dist\run\run.exe.manifest
copy hidpi.manifest dist\run\run.exe.manifest

View File

@ -1,7 +1,7 @@
@echo off
rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD%
pyinstaller -w --add-binary "mpv-2.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\trickplay.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
pyinstaller -w --add-binary "mpv-2.dll;." --add-data "jellyfin_mpv_shim\systray.png;jellyfin_mpv_shim" --hidden-import pystray._win32 --add-data "jellyfin_mpv_shim\mouse.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\trickplay-osc.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\thumbfast.lua;jellyfin_mpv_shim" --add-data "jellyfin_mpv_shim\default_shader_pack;jellyfin_mpv_shim\default_shader_pack" --add-data "jellyfin_mpv_shim\messages;jellyfin_mpv_shim\messages" --add-data "jellyfin_mpv_shim\display_mirror\index.html;jellyfin_mpv_shim\display_mirror" --add-data "jellyfin_mpv_shim\display_mirror\jellyfin.css;jellyfin_mpv_shim\display_mirror" --add-binary "Microsoft.Toolkit.Forms.UI.Controls.WebView.dll;." --icon jellyfin.ico run.py
if %errorlevel% neq 0 exit /b %errorlevel%
del dist\run\run.exe.manifest
copy hidpi.manifest dist\run\run.exe.manifest

View File

@ -128,7 +128,7 @@ class Settings(SettingsBase):
skip_intro_prompt: bool = False
thumbnail_enable: bool = True
thumbnail_jellyscrub: bool = False
thumbnail_custom_script: Optional[str] = None
thumbnail_osc_builtin: bool = True
thumbnail_preferred_size: int = 320
def __get_file(self, path: str, mode: str = "r", create: bool = True):

View File

@ -176,10 +176,10 @@ class PlayerManager(object):
self.trickplay = TrickPlay(self)
self.trickplay.start()
if settings.thumbnail_custom_script:
scripts.append(settings.thumbnail_custom_script)
else:
scripts.append(get_resource("trickplay.lua"))
scripts.append(get_resource("thumbfast.lua"))
if settings.thumbnail_osc_builtin:
scripts.append(get_resource("trickplay-osc.lua"))
except Exception:
log.error("Could not enable trickplay.", exc_info=True)

View File

@ -0,0 +1,111 @@
local utils = require 'mp.utils'
img_count = 0
img_multiplier = 0
img_width = 0
img_height = 0
img_file = ""
img_last_frame = -1
img_is_shown = false
img_enabled = false
img_is_bif = false
img_chapters = {}
img_overlay_id = 46
function send_thumbfast_message()
local json, err = utils.format_json({
width = img_width,
height = img_height,
disabled = not img_enabled,
available = img_enabled,
overlay_id = img_overlay_id
})
if err ~= nil
then
mp.log("error", "Failed to format JSON: " .. err)
else
mp.commandv("script-message", "thumbfast-info", json)
end
end
function client_message_handler(event)
local event_name = event["args"][1]
if event_name == "shim-trickplay-clear"
then
mp.log("info", "Clearing trickplay.")
img_enabled = false
if img_is_shown
then
mp.commandv("overlay-remove", 46)
img_is_shown = false
end
send_thumbfast_message()
elseif event_name == "shim-trickplay-bif"
then
mp.log("info", "Received BIF data.")
img_count = tonumber(event["args"][2])
img_multiplier = tonumber(event["args"][3])
img_width = tonumber(event["args"][4])
img_height = tonumber(event["args"][5])
img_file = event["args"][6]
img_last_frame = -1
img_enabled = true
img_is_bif = true
send_thumbfast_message()
elseif event_name == "shim-trickplay-chapters"
then
mp.log("info", "Received chapter metadata.")
img_width = tonumber(event["args"][2])
img_height = tonumber(event["args"][3])
img_file = event["args"][4]
img_chapters = {}
for timestamp in string.gmatch(event["args"][5], '([^,]+)') do
table.insert(img_chapters, tonumber(timestamp))
end
img_last_frame = -1
img_enabled = true
img_is_bif = false
send_thumbfast_message()
elseif event_name == "thumb"
then
local offset_seconds = tonumber(event["args"][2])
local x = tonumber(event["args"][3])
local y = tonumber(event["args"][4])
if offset_seconds == nil or x == nil or y == nil then
return
end
if img_enabled then
local frame = 0;
if img_is_bif then
frame = math.floor(offset_seconds / (img_multiplier / 1000))
else
for i = #img_chapters, 1, -1 do
if img_chapters[i] <= offset_seconds then
frame = i - 1
break
end
end
end
should_render_preview = true
if frame ~= img_last_frame then
if img_is_bif and frame >= img_count then
frame = img_count -1
end
local offset = frame * img_width * img_height * 4
img_is_shown = true
mp.commandv("overlay-add", img_overlay_id, x, y, img_file, offset, "bgra", img_width, img_height, img_width * 4)
end
end
elseif event_name == "clear"
then
if img_is_shown
then
mp.commandv("overlay-remove", img_overlay_id)
img_is_shown = false
end
end
end
mp.register_event("client-message", client_message_handler)

View File

@ -2,65 +2,29 @@
-- Sadly there is no way to do this without forking the entire OSC script.
-- All of my changes are marked with -- BEGIN patch and -- END patch.
-- BEGIN patch add thumbnails
img_count = 0
img_multiplier = 0
img_width = 0
img_height = 132
img_file = ""
img_last_frame = -1
img_is_shown = false
img_enabled = false
img_is_bif = false
img_chapters = {}
function client_message_handler(event)
local event_name = event["args"][1]
if event_name == "shim-trickplay-clear"
then
mp.log("info", "Clearing trickplay.")
img_enabled = false
if img_is_shown
then
mp.commandv("overlay-remove", 46)
img_is_shown = false
end
elseif event_name == "shim-trickplay-bif"
then
mp.log("info", "Received BIF data.")
img_count = tonumber(event["args"][2])
img_multiplier = tonumber(event["args"][3])
img_width = tonumber(event["args"][4])
img_height = tonumber(event["args"][5])
img_file = event["args"][6]
img_last_frame = -1
img_enabled = true
img_is_bif = true
elseif event_name == "shim-trickplay-chapters"
then
mp.log("info", "Received chapter metadata.")
img_width = tonumber(event["args"][2])
img_height = tonumber(event["args"][3])
img_file = event["args"][4]
img_chapters = {}
for timestamp in string.gmatch(event["args"][5], '([^,]+)') do
table.insert(img_chapters, tonumber(timestamp))
end
img_last_frame = -1
img_enabled = true
img_is_bif = false
end
end
mp.register_event("client-message", client_message_handler)
-- END patch add thumbnails
local assdraw = require 'mp.assdraw'
local msg = require 'mp.msg'
local opt = require 'mp.options'
local utils = require 'mp.utils'
-- BEGIN patch add thumbnails
local thumbfast = {
width = 0,
height = 0,
disabled = true,
available = false
}
mp.register_script_message("thumbfast-info", function(json)
local data = utils.parse_json(json)
if type(data) ~= "table" or not data.width or not data.height then
msg.error("thumbfast-info: received json didn't produce a table with thumbnail information")
else
thumbfast = data
end
end)
-- END patch add thumbnails
--
-- Parameters
--
@ -896,39 +860,23 @@ function render_elements(master_ass)
-- BEGIN patch add thumbnails
local duration = mp.get_property_number("duration", nil)
if img_enabled and not ((duration == nil) or (sliderpos == nil)) then
local frame = 0;
if img_is_bif then
frame = math.floor(duration * (sliderpos / 100) / (img_multiplier / 1000))
else
local current_time = duration * (sliderpos / 100)
for i = #img_chapters, 1, -1 do
if img_chapters[i] <= current_time then
frame = i - 1
break
end
end
end
if thumbfast.available and not ((duration == nil) or (sliderpos == nil)) then
should_render_preview = true
if frame ~= img_last_frame then
if img_is_bif and frame >= img_count then
frame = img_count -1
end
local offset = frame * img_width * img_height * 4
-- 46 is a random number, but hopefully it won't conflict with other extensions
local sx, sy = get_virt_scale_factor()
local mX, mY = get_virt_mouse_pos()
img_is_shown = true
mp.commandv("overlay-add", 46, math.floor(mX / sx - img_width / 2), math.floor((element.hitbox.y1 - 24) / sy - img_height), img_file, offset, "bgra", img_width, img_height, img_width * 4)
end
mp.commandv("script-message-to", "thumbfast", "thumb",
duration * (sliderpos / 100),
math.floor(mX / sx - thumbfast.width / 2),
math.floor((element.hitbox.y1 - 24) / sy - thumbfast.height)
)
end
-- END patch add thumbnails
end
end
-- BEGIN patch add thumbnails
if img_is_shown and not should_render_preview then
mp.commandv("overlay-remove", 46)
if thumbfast.available and not should_render_preview then
mp.commandv("script-message-to", "thumbfast", "clear")
end
-- END patch add thumbnails
@ -2562,8 +2510,8 @@ function render()
render_elements(ass)
else
-- BEGIN patch add thumbnails
if img_is_shown then
mp.commandv("overlay-remove", 46)
if thumbfast.available then
mp.commandv("script-message-to", "thumbfast", "clear")
end
-- END patch add thumbnails
end