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/default_shader_pack *
recursive-include jellyfin_mpv_shim/messages *.mo recursive-include jellyfin_mpv_shim/messages *.mo
include jellyfin_mpv_shim/mouse.lua 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 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 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 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` overriding the default MPV OSC, which may conflict with some custom user script. Trickplay is compatible
file contains the feature if you want to make a modified version. 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_enable` - Enable thumbnail feature. (Default: `true`)
- `thumbnail_jellyscrub` - Use JellyScrub as the thumbnail source instead of chapter images. (Default: `false`) - `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`) - `thumbnail_preferred_size` - The ideal size for thumbnails. (Default: `320`)
### SVP Integration ### SVP Integration

View File

@ -1,7 +1,7 @@
@echo off @echo off
rd /s /q __pycache__ dist build rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD% 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% if %errorlevel% neq 0 exit /b %errorlevel%
del dist\run\run.exe.manifest del dist\run\run.exe.manifest
copy hidpi.manifest dist\run\run.exe.manifest copy hidpi.manifest dist\run\run.exe.manifest

View File

@ -1,7 +1,7 @@
@echo off @echo off
rd /s /q __pycache__ dist build rd /s /q __pycache__ dist build
set PATH=%PATH%;%CD% 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% if %errorlevel% neq 0 exit /b %errorlevel%
del dist\run\run.exe.manifest del dist\run\run.exe.manifest
copy hidpi.manifest 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 skip_intro_prompt: bool = False
thumbnail_enable: bool = True thumbnail_enable: bool = True
thumbnail_jellyscrub: bool = False thumbnail_jellyscrub: bool = False
thumbnail_custom_script: Optional[str] = None thumbnail_osc_builtin: bool = True
thumbnail_preferred_size: int = 320 thumbnail_preferred_size: int = 320
def __get_file(self, path: str, mode: str = "r", create: bool = True): 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 = TrickPlay(self)
self.trickplay.start() self.trickplay.start()
if settings.thumbnail_custom_script: scripts.append(get_resource("thumbfast.lua"))
scripts.append(settings.thumbnail_custom_script) if settings.thumbnail_osc_builtin:
else: scripts.append(get_resource("trickplay-osc.lua"))
scripts.append(get_resource("trickplay.lua"))
except Exception: except Exception:
log.error("Could not enable trickplay.", exc_info=True) 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. -- 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. -- 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 assdraw = require 'mp.assdraw'
local msg = require 'mp.msg' local msg = require 'mp.msg'
local opt = require 'mp.options' local opt = require 'mp.options'
local utils = require 'mp.utils' 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 -- Parameters
-- --
@ -896,39 +860,23 @@ function render_elements(master_ass)
-- BEGIN patch add thumbnails -- BEGIN patch add thumbnails
local duration = mp.get_property_number("duration", nil) local duration = mp.get_property_number("duration", nil)
if img_enabled and not ((duration == nil) or (sliderpos == nil)) then if thumbfast.available 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
should_render_preview = true should_render_preview = true
if frame ~= img_last_frame then local sx, sy = get_virt_scale_factor()
if img_is_bif and frame >= img_count then local mX, mY = get_virt_mouse_pos()
frame = img_count -1 mp.commandv("script-message-to", "thumbfast", "thumb",
end duration * (sliderpos / 100),
local offset = frame * img_width * img_height * 4 math.floor(mX / sx - thumbfast.width / 2),
-- 46 is a random number, but hopefully it won't conflict with other extensions math.floor((element.hitbox.y1 - 24) / sy - thumbfast.height)
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
end end
-- END patch add thumbnails -- END patch add thumbnails
end end
end end
-- BEGIN patch add thumbnails -- BEGIN patch add thumbnails
if img_is_shown and not should_render_preview then if thumbfast.available and not should_render_preview then
mp.commandv("overlay-remove", 46) mp.commandv("script-message-to", "thumbfast", "clear")
end end
-- END patch add thumbnails -- END patch add thumbnails
@ -2562,8 +2510,8 @@ function render()
render_elements(ass) render_elements(ass)
else else
-- BEGIN patch add thumbnails -- BEGIN patch add thumbnails
if img_is_shown then if thumbfast.available then
mp.commandv("overlay-remove", 46) mp.commandv("script-message-to", "thumbfast", "clear")
end end
-- END patch add thumbnails -- END patch add thumbnails
end end