summaryrefslogtreecommitdiff
path: root/ar/.config/mpv/scripts/thumbfast.lua
diff options
context:
space:
mode:
authorTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-03-14 00:07:41 +0900
committerTheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com>2026-03-14 00:07:41 +0900
commit5e954f3dfb4304341c1a50a1f9310f58dcf39682 (patch)
treef4e5c1fdc9ee24b662fd9f4f43da4621a012c29a /ar/.config/mpv/scripts/thumbfast.lua
parenta13bf1ecb918bcf80ecb8bb9663ac7a84d576243 (diff)
updates
Diffstat (limited to 'ar/.config/mpv/scripts/thumbfast.lua')
-rw-r--r--ar/.config/mpv/scripts/thumbfast.lua1603
1 files changed, 898 insertions, 705 deletions
diff --git a/ar/.config/mpv/scripts/thumbfast.lua b/ar/.config/mpv/scripts/thumbfast.lua
index 58d1870..70c192b 100644
--- a/ar/.config/mpv/scripts/thumbfast.lua
+++ b/ar/.config/mpv/scripts/thumbfast.lua
@@ -11,51 +11,51 @@ file, You can obtain one at https://mozilla.org/MPL/2.0/.
]]
local options = {
- -- Socket path (leave empty for auto)
- socket = "",
+ -- Socket path (leave empty for auto)
+ socket = "",
- -- Thumbnail path (leave empty for auto)
- thumbnail = "",
+ -- Thumbnail path (leave empty for auto)
+ thumbnail = "",
- -- Maximum thumbnail generation size in pixels (scaled down to fit)
- -- Values are scaled when hidpi is enabled
- max_height = 200,
- max_width = 200,
+ -- Maximum thumbnail generation size in pixels (scaled down to fit)
+ -- Values are scaled when hidpi is enabled
+ max_height = 200,
+ max_width = 200,
- -- Scale factor for thumbnail display size (requires mpv 0.38+)
- -- Note that this is lower quality than increasing max_height and max_width
- scale_factor = 1,
+ -- Scale factor for thumbnail display size (requires mpv 0.38+)
+ -- Note that this is lower quality than increasing max_height and max_width
+ scale_factor = 1,
- -- Apply tone-mapping, no to disable
- tone_mapping = "auto",
+ -- Apply tone-mapping, no to disable
+ tone_mapping = "auto",
- -- Overlay id
- overlay_id = 42,
+ -- Overlay id
+ overlay_id = 42,
- -- Spawn thumbnailer on file load for faster initial thumbnails
- spawn_first = false,
+ -- Spawn thumbnailer on file load for faster initial thumbnails
+ spawn_first = false,
- -- Close thumbnailer process after an inactivity period in seconds, 0 to disable
- quit_after_inactivity = 0,
+ -- Close thumbnailer process after an inactivity period in seconds, 0 to disable
+ quit_after_inactivity = 0,
- -- Enable on network playback
- network = false,
+ -- Enable on network playback
+ network = false,
- -- Enable on audio playback
- audio = false,
+ -- Enable on audio playback
+ audio = false,
- -- Enable hardware decoding
- hwdec = false,
+ -- Enable hardware decoding
+ hwdec = false,
- -- Windows only: use native Windows API to write to pipe (requires LuaJIT)
- direct_io = false,
+ -- Windows only: use native Windows API to write to pipe (requires LuaJIT)
+ direct_io = false,
- -- Custom path to the mpv executable
- mpv_path = "mpv"
+ -- Custom path to the mpv executable
+ mpv_path = "mpv",
}
-mp.utils = require "mp.utils"
-mp.options = require "mp.options"
+mp.utils = require("mp.utils")
+mp.options = require("mp.options")
mp.options.read_options(options, "thumbfast")
local properties = {}
@@ -64,73 +64,72 @@ local pre_0_33_0 = true
local support_media_control = mp.get_property_native("media-controls") ~= nil
function subprocess(args, async, callback)
- callback = callback or function() end
-
- if not pre_0_30_0 then
- if async then
- return mp.command_native_async({name = "subprocess", playback_only = true, args = args}, callback)
- else
- return mp.command_native({name = "subprocess", playback_only = false, capture_stdout = true, args = args})
- end
- else
- if async then
- return mp.utils.subprocess_detached({args = args}, callback)
- else
- return mp.utils.subprocess({args = args})
- end
- end
+ callback = callback or function() end
+
+ if not pre_0_30_0 then
+ if async then
+ return mp.command_native_async({ name = "subprocess", playback_only = true, args = args }, callback)
+ else
+ return mp.command_native({ name = "subprocess", playback_only = false, capture_stdout = true, args = args })
+ end
+ else
+ if async then
+ return mp.utils.subprocess_detached({ args = args }, callback)
+ else
+ return mp.utils.subprocess({ args = args })
+ end
+ end
end
local winapi = {}
if options.direct_io then
- local ffi_loaded, ffi = pcall(require, "ffi")
- if ffi_loaded then
- winapi = {
- ffi = ffi,
- C = ffi.C,
- bit = require("bit"),
- socket_wc = "",
-
- -- WinAPI constants
- CP_UTF8 = 65001,
- GENERIC_WRITE = 0x40000000,
- OPEN_EXISTING = 3,
- FILE_FLAG_WRITE_THROUGH = 0x80000000,
- FILE_FLAG_NO_BUFFERING = 0x20000000,
- PIPE_NOWAIT = ffi.new("unsigned long[1]", 0x00000001),
-
- INVALID_HANDLE_VALUE = ffi.cast("void*", -1),
-
- -- don't care about how many bytes WriteFile wrote, so allocate something to store the result once
- _lpNumberOfBytesWritten = ffi.new("unsigned long[1]"),
- }
- -- cache flags used in run() to avoid bor() call
- winapi._createfile_pipe_flags = winapi.bit.bor(winapi.FILE_FLAG_WRITE_THROUGH, winapi.FILE_FLAG_NO_BUFFERING)
-
- ffi.cdef[[
+ local ffi_loaded, ffi = pcall(require, "ffi")
+ if ffi_loaded then
+ winapi = {
+ ffi = ffi,
+ C = ffi.C,
+ bit = require("bit"),
+ socket_wc = "",
+
+ -- WinAPI constants
+ CP_UTF8 = 65001,
+ GENERIC_WRITE = 0x40000000,
+ OPEN_EXISTING = 3,
+ FILE_FLAG_WRITE_THROUGH = 0x80000000,
+ FILE_FLAG_NO_BUFFERING = 0x20000000,
+ PIPE_NOWAIT = ffi.new("unsigned long[1]", 0x00000001),
+
+ INVALID_HANDLE_VALUE = ffi.cast("void*", -1),
+
+ -- don't care about how many bytes WriteFile wrote, so allocate something to store the result once
+ _lpNumberOfBytesWritten = ffi.new("unsigned long[1]"),
+ }
+ -- cache flags used in run() to avoid bor() call
+ winapi._createfile_pipe_flags = winapi.bit.bor(winapi.FILE_FLAG_WRITE_THROUGH, winapi.FILE_FLAG_NO_BUFFERING)
+
+ ffi.cdef([[
void* __stdcall CreateFileW(const wchar_t *lpFileName, unsigned long dwDesiredAccess, unsigned long dwShareMode, void *lpSecurityAttributes, unsigned long dwCreationDisposition, unsigned long dwFlagsAndAttributes, void *hTemplateFile);
bool __stdcall WriteFile(void *hFile, const void *lpBuffer, unsigned long nNumberOfBytesToWrite, unsigned long *lpNumberOfBytesWritten, void *lpOverlapped);
bool __stdcall CloseHandle(void *hObject);
bool __stdcall SetNamedPipeHandleState(void *hNamedPipe, unsigned long *lpMode, unsigned long *lpMaxCollectionCount, unsigned long *lpCollectDataTimeout);
int __stdcall MultiByteToWideChar(unsigned int CodePage, unsigned long dwFlags, const char *lpMultiByteStr, int cbMultiByte, wchar_t *lpWideCharStr, int cchWideChar);
- ]]
-
- winapi.MultiByteToWideChar = function(MultiByteStr)
- if MultiByteStr then
- local utf16_len = winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, nil, 0)
- if utf16_len > 0 then
- local utf16_str = winapi.ffi.new("wchar_t[?]", utf16_len)
- if winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, utf16_str, utf16_len) > 0 then
- return utf16_str
- end
- end
- end
- return ""
- end
-
- else
- options.direct_io = false
- end
+ ]])
+
+ winapi.MultiByteToWideChar = function(MultiByteStr)
+ if MultiByteStr then
+ local utf16_len = winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, nil, 0)
+ if utf16_len > 0 then
+ local utf16_str = winapi.ffi.new("wchar_t[?]", utf16_len)
+ if winapi.C.MultiByteToWideChar(winapi.CP_UTF8, 0, MultiByteStr, -1, utf16_str, utf16_len) > 0 then
+ return utf16_str
+ end
+ end
+ end
+ return ""
+ end
+ else
+ options.direct_io = false
+ end
end
local file
@@ -157,11 +156,19 @@ local script_name
local show_thumbnail = false
-local filters_reset = {["lavfi-crop"]=true, ["crop"]=true}
-local filters_runtime = {["hflip"]=true, ["vflip"]=true}
-local filters_all = {["hflip"]=true, ["vflip"]=true, ["lavfi-crop"]=true, ["crop"]=true}
-
-local tone_mappings = {["none"]=true, ["clip"]=true, ["linear"]=true, ["gamma"]=true, ["reinhard"]=true, ["hable"]=true, ["mobius"]=true}
+local filters_reset = { ["lavfi-crop"] = true, ["crop"] = true }
+local filters_runtime = { ["hflip"] = true, ["vflip"] = true }
+local filters_all = { ["hflip"] = true, ["vflip"] = true, ["lavfi-crop"] = true, ["crop"] = true }
+
+local tone_mappings = {
+ ["none"] = true,
+ ["clip"] = true,
+ ["linear"] = true,
+ ["gamma"] = true,
+ ["reinhard"] = true,
+ ["hable"] = true,
+ ["mobius"] = true,
+}
local last_tone_mapping
local last_vf_reset = ""
@@ -178,7 +185,7 @@ local last_has_vid = 0
local has_vid = 0
local file_timer
-local file_check_period = 1/60
+local file_check_period = 1 / 60
local allow_fast_seek = true
@@ -191,50 +198,50 @@ if echo "print-text thumbfast" >&"$MPV_IPC_FD"; then echo -n > "$MPV_IPC_PATH";
]=]
local function get_os()
- local raw_os_name = ""
-
- if jit and jit.os and jit.arch then
- raw_os_name = jit.os
- else
- if package.config:sub(1,1) == "\\" then
- -- Windows
- local env_OS = os.getenv("OS")
- if env_OS then
- raw_os_name = env_OS
- end
- else
- raw_os_name = subprocess({"uname", "-s"}).stdout
- end
- end
-
- raw_os_name = (raw_os_name):lower()
-
- local os_patterns = {
- ["windows"] = "windows",
- ["linux"] = "linux",
-
- ["osx"] = "darwin",
- ["mac"] = "darwin",
- ["darwin"] = "darwin",
-
- ["^mingw"] = "windows",
- ["^cygwin"] = "windows",
-
- ["bsd$"] = "darwin",
- ["sunos"] = "darwin"
- }
-
- -- Default to linux
- local str_os_name = "linux"
-
- for pattern, name in pairs(os_patterns) do
- if raw_os_name:match(pattern) then
- str_os_name = name
- break
- end
- end
-
- return str_os_name
+ local raw_os_name = ""
+
+ if jit and jit.os and jit.arch then
+ raw_os_name = jit.os
+ else
+ if package.config:sub(1, 1) == "\\" then
+ -- Windows
+ local env_OS = os.getenv("OS")
+ if env_OS then
+ raw_os_name = env_OS
+ end
+ else
+ raw_os_name = subprocess({ "uname", "-s" }).stdout
+ end
+ end
+
+ raw_os_name = (raw_os_name):lower()
+
+ local os_patterns = {
+ ["windows"] = "windows",
+ ["linux"] = "linux",
+
+ ["osx"] = "darwin",
+ ["mac"] = "darwin",
+ ["darwin"] = "darwin",
+
+ ["^mingw"] = "windows",
+ ["^cygwin"] = "windows",
+
+ ["bsd$"] = "darwin",
+ ["sunos"] = "darwin",
+ }
+
+ -- Default to linux
+ local str_os_name = "linux"
+
+ for pattern, name in pairs(os_patterns) do
+ if raw_os_name:match(pattern) then
+ str_os_name = name
+ break
+ end
+ end
+
+ return str_os_name
end
local os_name = mp.get_property("platform") or get_os()
@@ -242,19 +249,19 @@ local os_name = mp.get_property("platform") or get_os()
local path_separator = os_name == "windows" and "\\" or "/"
if options.socket == "" then
- if os_name == "windows" then
- options.socket = "thumbfast"
- else
- options.socket = "/tmp/thumbfast"
- end
+ if os_name == "windows" then
+ options.socket = "thumbfast"
+ else
+ options.socket = "/tmp/thumbfast"
+ end
end
if options.thumbnail == "" then
- if os_name == "windows" then
- options.thumbnail = os.getenv("TEMP").."\\thumbfast.out"
- else
- options.thumbnail = "/tmp/thumbfast.out"
- end
+ if os_name == "windows" then
+ options.thumbnail = os.getenv("TEMP") .. "\\thumbfast.out"
+ else
+ options.thumbnail = "/tmp/thumbfast.out"
+ end
end
local unique = mp.utils.getpid()
@@ -263,13 +270,13 @@ options.socket = options.socket .. unique
options.thumbnail = options.thumbnail .. unique
if options.direct_io then
- if os_name == "windows" then
- winapi.socket_wc = winapi.MultiByteToWideChar("\\\\.\\pipe\\" .. options.socket)
- end
+ if os_name == "windows" then
+ winapi.socket_wc = winapi.MultiByteToWideChar("\\\\.\\pipe\\" .. options.socket)
+ end
- if winapi.socket_wc == "" then
- options.direct_io = false
- end
+ if winapi.socket_wc == "" then
+ options.direct_io = false
+ end
end
options.scale_factor = math.floor(options.scale_factor)
@@ -278,651 +285,837 @@ local mpv_path = options.mpv_path
local frontend_path
if mpv_path == "mpv" and os_name == "windows" then
- frontend_path = mp.get_property_native("user-data/frontend/process-path")
- mpv_path = frontend_path or mpv_path
+ frontend_path = mp.get_property_native("user-data/frontend/process-path")
+ mpv_path = frontend_path or mpv_path
end
if mpv_path == "mpv" and os_name == "darwin" and unique then
- -- TODO: look into ~~osxbundle/
- mpv_path = string.gsub(subprocess({"ps", "-o", "comm=", "-p", tostring(unique)}).stdout, "[\n\r]", "")
- if mpv_path ~= "mpv" then
- mpv_path = string.gsub(mpv_path, "/mpv%-bundle$", "/mpv")
- local mpv_bin = mp.utils.file_info("/usr/local/mpv")
- if mpv_bin and mpv_bin.is_file then
- mpv_path = "/usr/local/mpv"
- else
- local mpv_app = mp.utils.file_info("/Applications/mpv.app/Contents/MacOS/mpv")
- if mpv_app and mpv_app.is_file then
- mp.msg.warn("symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`")
- else
- mp.msg.warn("drag to your Applications folder and symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`")
- end
- end
- end
+ -- TODO: look into ~~osxbundle/
+ mpv_path = string.gsub(subprocess({ "ps", "-o", "comm=", "-p", tostring(unique) }).stdout, "[\n\r]", "")
+ if mpv_path ~= "mpv" then
+ mpv_path = string.gsub(mpv_path, "/mpv%-bundle$", "/mpv")
+ local mpv_bin = mp.utils.file_info("/usr/local/mpv")
+ if mpv_bin and mpv_bin.is_file then
+ mpv_path = "/usr/local/mpv"
+ else
+ local mpv_app = mp.utils.file_info("/Applications/mpv.app/Contents/MacOS/mpv")
+ if mpv_app and mpv_app.is_file then
+ mp.msg.warn(
+ "symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`"
+ )
+ else
+ mp.msg.warn(
+ "drag to your Applications folder and symlink mpv to fix Dock icons: `sudo ln -s /Applications/mpv.app/Contents/MacOS/mpv /usr/local/mpv`"
+ )
+ end
+ end
+ end
end
local function vo_tone_mapping()
- local passes = mp.get_property_native("vo-passes")
- if passes and passes["fresh"] then
- for k, v in pairs(passes["fresh"]) do
- for k2, v2 in pairs(v) do
- if k2 == "desc" and v2 then
- local tone_mapping = string.match(v2, "([0-9a-z.-]+) tone map")
- if tone_mapping then
- return tone_mapping
- end
- end
- end
- end
- end
+ local passes = mp.get_property_native("vo-passes")
+ if passes and passes["fresh"] then
+ for k, v in pairs(passes["fresh"]) do
+ for k2, v2 in pairs(v) do
+ if k2 == "desc" and v2 then
+ local tone_mapping = string.match(v2, "([0-9a-z.-]+) tone map")
+ if tone_mapping then
+ return tone_mapping
+ end
+ end
+ end
+ end
+ end
end
local function vf_string(filters, full)
- local vf = ""
- local vf_table = properties["vf"]
-
- if (properties["video-crop"] or "") ~= "" then
- vf = "lavfi-crop="..string.gsub(properties["video-crop"], "(%d*)x?(%d*)%+(%d+)%+(%d+)", "w=%1:h=%2:x=%3:y=%4")..","
- local width = properties["video-out-params"] and properties["video-out-params"]["dw"]
- local height = properties["video-out-params"] and properties["video-out-params"]["dh"]
- if width and height then
- vf = string.gsub(vf, "w=:h=:", "w="..width..":h="..height..":")
- end
- end
-
- if vf_table and #vf_table > 0 then
- for i = #vf_table, 1, -1 do
- if filters[vf_table[i].name] then
- local args = ""
- for key, value in pairs(vf_table[i].params) do
- if args ~= "" then
- args = args .. ":"
- end
- args = args .. key .. "=" .. value
- end
- vf = vf .. vf_table[i].name .. "=" .. args .. ","
- end
- end
- end
-
- if (full and options.tone_mapping ~= "no") or options.tone_mapping == "auto" then
- if properties["video-params"] and properties["video-params"]["primaries"] == "bt.2020" then
- local tone_mapping = options.tone_mapping
- if tone_mapping == "auto" then
- tone_mapping = last_tone_mapping or properties["tone-mapping"]
- if tone_mapping == "auto" and properties["current-vo"] == "gpu-next" then
- tone_mapping = vo_tone_mapping()
- end
- end
- if not tone_mappings[tone_mapping] then
- tone_mapping = "hable"
- end
- last_tone_mapping = tone_mapping
- vf = vf .. "zscale=transfer=linear,format=gbrpf32le,tonemap="..tone_mapping..",zscale=transfer=bt709,"
- end
- end
-
- if full then
- vf = vf.."scale=w="..effective_w..":h="..effective_h..par..",pad=w="..effective_w..":h="..effective_h..":x=-1:y=-1,format=bgra"
- end
-
- return vf
+ local vf = ""
+ local vf_table = properties["vf"]
+
+ if (properties["video-crop"] or "") ~= "" then
+ vf = "lavfi-crop="
+ .. string.gsub(properties["video-crop"], "(%d*)x?(%d*)%+(%d+)%+(%d+)", "w=%1:h=%2:x=%3:y=%4")
+ .. ","
+ local width = properties["video-out-params"] and properties["video-out-params"]["dw"]
+ local height = properties["video-out-params"] and properties["video-out-params"]["dh"]
+ if width and height then
+ vf = string.gsub(vf, "w=:h=:", "w=" .. width .. ":h=" .. height .. ":")
+ end
+ end
+
+ if vf_table and #vf_table > 0 then
+ for i = #vf_table, 1, -1 do
+ if filters[vf_table[i].name] then
+ local args = ""
+ for key, value in pairs(vf_table[i].params) do
+ if args ~= "" then
+ args = args .. ":"
+ end
+ args = args .. key .. "=" .. value
+ end
+ vf = vf .. vf_table[i].name .. "=" .. args .. ","
+ end
+ end
+ end
+
+ if (full and options.tone_mapping ~= "no") or options.tone_mapping == "auto" then
+ if properties["video-params"] and properties["video-params"]["primaries"] == "bt.2020" then
+ local tone_mapping = options.tone_mapping
+ if tone_mapping == "auto" then
+ tone_mapping = last_tone_mapping or properties["tone-mapping"]
+ if tone_mapping == "auto" and properties["current-vo"] == "gpu-next" then
+ tone_mapping = vo_tone_mapping()
+ end
+ end
+ if not tone_mappings[tone_mapping] then
+ tone_mapping = "hable"
+ end
+ last_tone_mapping = tone_mapping
+ vf = vf .. "zscale=transfer=linear,format=gbrpf32le,tonemap=" .. tone_mapping .. ",zscale=transfer=bt709,"
+ end
+ end
+
+ if full then
+ vf = vf
+ .. "scale=w="
+ .. effective_w
+ .. ":h="
+ .. effective_h
+ .. par
+ .. ",pad=w="
+ .. effective_w
+ .. ":h="
+ .. effective_h
+ .. ":x=-1:y=-1,format=bgra"
+ end
+
+ return vf
end
local function calc_dimensions()
- local width = properties["video-out-params"] and properties["video-out-params"]["dw"]
- local height = properties["video-out-params"] and properties["video-out-params"]["dh"]
- if not width or not height then return end
-
- local scale = properties["display-hidpi-scale"] or 1
-
- if width / height > options.max_width / options.max_height then
- effective_w = math.floor(options.max_width * scale + 0.5)
- effective_h = math.floor(height / width * effective_w + 0.5)
- else
- effective_h = math.floor(options.max_height * scale + 0.5)
- effective_w = math.floor(width / height * effective_h + 0.5)
- end
-
- local v_par = properties["video-out-params"] and properties["video-out-params"]["par"] or 1
- if v_par == 1 then
- par = ":force_original_aspect_ratio=decrease"
- else
- par = ""
- end
+ local width = properties["video-out-params"] and properties["video-out-params"]["dw"]
+ local height = properties["video-out-params"] and properties["video-out-params"]["dh"]
+ if not width or not height then
+ return
+ end
+
+ local scale = properties["display-hidpi-scale"] or 1
+
+ if width / height > options.max_width / options.max_height then
+ effective_w = math.floor(options.max_width * scale + 0.5)
+ effective_h = math.floor(height / width * effective_w + 0.5)
+ else
+ effective_h = math.floor(options.max_height * scale + 0.5)
+ effective_w = math.floor(width / height * effective_h + 0.5)
+ end
+
+ local v_par = properties["video-out-params"] and properties["video-out-params"]["par"] or 1
+ if v_par == 1 then
+ par = ":force_original_aspect_ratio=decrease"
+ else
+ par = ""
+ end
end
local info_timer = nil
local function info(w, h)
- local rotate = properties["video-params"] and properties["video-params"]["rotate"]
- local image = properties["current-tracks/video"] and properties["current-tracks/video"]["image"]
- local albumart = image and properties["current-tracks/video"]["albumart"]
-
- disabled = (w or 0) == 0 or (h or 0) == 0 or
- has_vid == 0 or
- (properties["demuxer-via-network"] and not options.network) or
- (albumart and not options.audio) or
- (image and not albumart) or
- force_disabled
-
- if info_timer then
- info_timer:kill()
- info_timer = nil
- elseif has_vid == 0 or (rotate == nil and not disabled) then
- info_timer = mp.add_timeout(0.05, function() info(w, h) end)
- end
-
- local json, err = mp.utils.format_json({width=w * options.scale_factor, height=h * options.scale_factor, scale_factor=options.scale_factor, disabled=disabled, available=true, socket=options.socket, thumbnail=options.thumbnail, overlay_id=options.overlay_id})
- if pre_0_30_0 then
- mp.command_native({"script-message", "thumbfast-info", json})
- else
- mp.command_native_async({"script-message", "thumbfast-info", json}, function() end)
- end
+ local rotate = properties["video-params"] and properties["video-params"]["rotate"]
+ local image = properties["current-tracks/video"] and properties["current-tracks/video"]["image"]
+ local albumart = image and properties["current-tracks/video"]["albumart"]
+
+ disabled = (w or 0) == 0
+ or (h or 0) == 0
+ or has_vid == 0
+ or (properties["demuxer-via-network"] and not options.network)
+ or (albumart and not options.audio)
+ or (image and not albumart)
+ or force_disabled
+
+ if info_timer then
+ info_timer:kill()
+ info_timer = nil
+ elseif has_vid == 0 or (rotate == nil and not disabled) then
+ info_timer = mp.add_timeout(0.05, function()
+ info(w, h)
+ end)
+ end
+
+ local json, err = mp.utils.format_json({
+ width = w * options.scale_factor,
+ height = h * options.scale_factor,
+ scale_factor = options.scale_factor,
+ disabled = disabled,
+ available = true,
+ socket = options.socket,
+ thumbnail = options.thumbnail,
+ overlay_id = options.overlay_id,
+ })
+ if pre_0_30_0 then
+ mp.command_native({ "script-message", "thumbfast-info", json })
+ else
+ mp.command_native_async({ "script-message", "thumbfast-info", json }, function() end)
+ end
end
local function remove_thumbnail_files()
- if file then
- file:close()
- file = nil
- file_bytes = 0
- end
- os.remove(options.thumbnail)
- os.remove(options.thumbnail..".bgra")
+ if file then
+ file:close()
+ file = nil
+ file_bytes = 0
+ end
+ os.remove(options.thumbnail)
+ os.remove(options.thumbnail .. ".bgra")
end
local activity_timer
local function spawn(time)
- if disabled then return end
-
- local path = properties["path"]
- if path == nil then return end
-
- if options.quit_after_inactivity > 0 then
- if show_thumbnail or activity_timer:is_enabled() then
- activity_timer:kill()
- end
- activity_timer:resume()
- end
-
- local open_filename = properties["stream-open-filename"]
- local ytdl = open_filename and properties["demuxer-via-network"] and path ~= open_filename
- if ytdl then
- path = open_filename
- end
-
- remove_thumbnail_files()
-
- local vid = properties["vid"]
- has_vid = vid or 0
-
- local args = {
- mpv_path, "--no-config", "--msg-level=all=no", "--idle", "--pause", "--keep-open=always", "--really-quiet", "--no-terminal",
- "--load-scripts=no", "--osc=no", "--ytdl=no", "--load-stats-overlay=no", "--load-osd-console=no", "--load-auto-profiles=no",
- "--edition="..(properties["edition"] or "auto"), "--vid="..(vid or "auto"), "--no-sub", "--no-audio",
- "--start="..time, allow_fast_seek and "--hr-seek=no" or "--hr-seek=yes",
- "--ytdl-format=worst", "--demuxer-readahead-secs=0", "--demuxer-max-bytes=128KiB",
- "--vd-lavc-skiploopfilter=all", "--vd-lavc-software-fallback=1", "--vd-lavc-fast", "--vd-lavc-threads=2", "--hwdec="..(options.hwdec and "auto" or "no"),
- "--vf="..vf_string(filters_all, true),
- "--sws-scaler=fast-bilinear",
- "--video-rotate="..last_rotate,
- "--ovc=rawvideo", "--of=image2", "--ofopts=update=1", "--o="..options.thumbnail
- }
-
- if not pre_0_30_0 then
- table.insert(args, "--sws-allow-zimg=no")
- end
-
- if support_media_control then
- table.insert(args, "--media-controls=no")
- end
-
- if os_name == "darwin" and properties["macos-app-activation-policy"] then
- table.insert(args, "--macos-app-activation-policy=accessory")
- end
-
- if os_name == "windows" or pre_0_33_0 then
- table.insert(args, "--input-ipc-server="..options.socket)
- elseif not script_written then
- local client_script_path = options.socket..".run"
- local script = io.open(client_script_path, "w+")
- if script == nil then
- mp.msg.error("client script write failed")
- return
- else
- script_written = true
- script:write(string.format(client_script, options.socket))
- script:close()
- subprocess({"chmod", "+x", client_script_path}, true)
- table.insert(args, "--scripts="..client_script_path)
- end
- else
- local client_script_path = options.socket..".run"
- table.insert(args, "--scripts="..client_script_path)
- end
-
- table.insert(args, "--")
- table.insert(args, path)
-
- spawned = true
- spawn_waiting = true
-
- subprocess(args, true,
- function(success, result)
- if spawn_waiting and (success == false or (result.status ~= 0 and result.status ~= -2)) then
- spawned = false
- spawn_waiting = false
- options.tone_mapping = "no"
- mp.msg.error("mpv subprocess create failed")
- if not spawn_working then -- notify users of required configuration
- if options.mpv_path == "mpv" then
- if properties["current-vo"] == "libmpv" then
- if options.mpv_path == mpv_path then -- attempt to locate ImPlay
- mpv_path = "ImPlay"
- spawn(time)
- else -- ImPlay not in path
- if os_name ~= "darwin" then
- force_disabled = true
- info(real_w or effective_w, real_h or effective_h)
- end
- mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
- mp.commandv("script-message-to", "implay", "show-message", "thumbfast initial setup", "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" .. string.gsub(mp.command_native({"expand-path", "~~/script-opts/thumbfast.conf"}), "[/\\]", path_separator).."\nand restart ImPlay")
- end
- else
- mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
- if os_name == "windows" and frontend_path == nil then
- mp.commandv("script-message-to", "mpvnet", "show-text", "thumbfast: ERROR! install standalone mpv, see README", 5000, 20)
- mp.commandv("script-message", "mpv.net", "show-text", "thumbfast: ERROR! install standalone mpv, see README", 5000, 20)
- end
- end
- else
- mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
- -- found ImPlay but not defined in config
- mp.commandv("script-message-to", "implay", "show-message", "thumbfast", "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n" .. string.gsub(mp.command_native({"expand-path", "~~/script-opts/thumbfast.conf"}), "[/\\]", path_separator).."\nand restart ImPlay")
- end
- end
- elseif success == true and (result.status == 0 or result.status == -2) then
- if not spawn_working and properties["current-vo"] == "libmpv" and options.mpv_path ~= mpv_path then
- mp.commandv("script-message-to", "implay", "show-message", "thumbfast initial setup", "Set mpv_path=ImPlay in thumbfast config:\n" .. string.gsub(mp.command_native({"expand-path", "~~/script-opts/thumbfast.conf"}), "[/\\]", path_separator).."\nand restart ImPlay")
- end
- spawn_working = true
- spawn_waiting = false
- end
- end
- )
+ if disabled then
+ return
+ end
+
+ local path = properties["path"]
+ if path == nil then
+ return
+ end
+
+ if options.quit_after_inactivity > 0 then
+ if show_thumbnail or activity_timer:is_enabled() then
+ activity_timer:kill()
+ end
+ activity_timer:resume()
+ end
+
+ local open_filename = properties["stream-open-filename"]
+ local ytdl = open_filename and properties["demuxer-via-network"] and path ~= open_filename
+ if ytdl then
+ path = open_filename
+ end
+
+ remove_thumbnail_files()
+
+ local vid = properties["vid"]
+ has_vid = vid or 0
+
+ local args = {
+ mpv_path,
+ "--no-config",
+ "--msg-level=all=no",
+ "--idle",
+ "--pause",
+ "--keep-open=always",
+ "--really-quiet",
+ "--no-terminal",
+ "--load-scripts=no",
+ "--osc=no",
+ "--ytdl=no",
+ "--load-stats-overlay=no",
+ "--load-osd-console=no",
+ "--load-auto-profiles=no",
+ "--edition=" .. (properties["edition"] or "auto"),
+ "--vid=" .. (vid or "auto"),
+ "--no-sub",
+ "--no-audio",
+ "--start=" .. time,
+ allow_fast_seek and "--hr-seek=no" or "--hr-seek=yes",
+ "--ytdl-format=worst",
+ "--demuxer-readahead-secs=0",
+ "--demuxer-max-bytes=128KiB",
+ "--vd-lavc-skiploopfilter=all",
+ "--vd-lavc-software-fallback=1",
+ "--vd-lavc-fast",
+ "--vd-lavc-threads=2",
+ "--hwdec=" .. (options.hwdec and "auto" or "no"),
+ "--vf=" .. vf_string(filters_all, true),
+ "--sws-scaler=fast-bilinear",
+ "--video-rotate=" .. last_rotate,
+ "--ovc=rawvideo",
+ "--of=image2",
+ "--ofopts=update=1",
+ "--o=" .. options.thumbnail,
+ }
+
+ if not pre_0_30_0 then
+ table.insert(args, "--sws-allow-zimg=no")
+ end
+
+ if support_media_control then
+ table.insert(args, "--media-controls=no")
+ end
+
+ if os_name == "darwin" and properties["macos-app-activation-policy"] then
+ table.insert(args, "--macos-app-activation-policy=accessory")
+ end
+
+ if os_name == "windows" or pre_0_33_0 then
+ table.insert(args, "--input-ipc-server=" .. options.socket)
+ elseif not script_written then
+ local client_script_path = options.socket .. ".run"
+ local script = io.open(client_script_path, "w+")
+ if script == nil then
+ mp.msg.error("client script write failed")
+ return
+ else
+ script_written = true
+ script:write(string.format(client_script, options.socket))
+ script:close()
+ subprocess({ "chmod", "+x", client_script_path }, true)
+ table.insert(args, "--scripts=" .. client_script_path)
+ end
+ else
+ local client_script_path = options.socket .. ".run"
+ table.insert(args, "--scripts=" .. client_script_path)
+ end
+
+ table.insert(args, "--")
+ table.insert(args, path)
+
+ spawned = true
+ spawn_waiting = true
+
+ subprocess(args, true, function(success, result)
+ if spawn_waiting and (success == false or (result.status ~= 0 and result.status ~= -2)) then
+ spawned = false
+ spawn_waiting = false
+ options.tone_mapping = "no"
+ mp.msg.error("mpv subprocess create failed")
+ if not spawn_working then -- notify users of required configuration
+ if options.mpv_path == "mpv" then
+ if properties["current-vo"] == "libmpv" then
+ if options.mpv_path == mpv_path then -- attempt to locate ImPlay
+ mpv_path = "ImPlay"
+ spawn(time)
+ else -- ImPlay not in path
+ if os_name ~= "darwin" then
+ force_disabled = true
+ info(real_w or effective_w, real_h or effective_h)
+ end
+ mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
+ mp.commandv(
+ "script-message-to",
+ "implay",
+ "show-message",
+ "thumbfast initial setup",
+ "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n"
+ .. string.gsub(
+ mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }),
+ "[/\\]",
+ path_separator
+ )
+ .. "\nand restart ImPlay"
+ )
+ end
+ else
+ mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
+ if os_name == "windows" and frontend_path == nil then
+ mp.commandv(
+ "script-message-to",
+ "mpvnet",
+ "show-text",
+ "thumbfast: ERROR! install standalone mpv, see README",
+ 5000,
+ 20
+ )
+ mp.commandv(
+ "script-message",
+ "mpv.net",
+ "show-text",
+ "thumbfast: ERROR! install standalone mpv, see README",
+ 5000,
+ 20
+ )
+ end
+ end
+ else
+ mp.commandv("show-text", "thumbfast: ERROR! cannot create mpv subprocess", 5000)
+ -- found ImPlay but not defined in config
+ mp.commandv(
+ "script-message-to",
+ "implay",
+ "show-message",
+ "thumbfast",
+ "Set mpv_path=PATH_TO_ImPlay in thumbfast config:\n"
+ .. string.gsub(
+ mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }),
+ "[/\\]",
+ path_separator
+ )
+ .. "\nand restart ImPlay"
+ )
+ end
+ end
+ elseif success == true and (result.status == 0 or result.status == -2) then
+ if not spawn_working and properties["current-vo"] == "libmpv" and options.mpv_path ~= mpv_path then
+ mp.commandv(
+ "script-message-to",
+ "implay",
+ "show-message",
+ "thumbfast initial setup",
+ "Set mpv_path=ImPlay in thumbfast config:\n"
+ .. string.gsub(
+ mp.command_native({ "expand-path", "~~/script-opts/thumbfast.conf" }),
+ "[/\\]",
+ path_separator
+ )
+ .. "\nand restart ImPlay"
+ )
+ end
+ spawn_working = true
+ spawn_waiting = false
+ end
+ end)
end
local function run(command)
- if not spawned then return end
-
- if options.direct_io then
- local hPipe = winapi.C.CreateFileW(winapi.socket_wc, winapi.GENERIC_WRITE, 0, nil, winapi.OPEN_EXISTING, winapi._createfile_pipe_flags, nil)
- if hPipe ~= winapi.INVALID_HANDLE_VALUE then
- local buf = command .. "\n"
- winapi.C.SetNamedPipeHandleState(hPipe, winapi.PIPE_NOWAIT, nil, nil)
- winapi.C.WriteFile(hPipe, buf, #buf + 1, winapi._lpNumberOfBytesWritten, nil)
- winapi.C.CloseHandle(hPipe)
- end
-
- return
- end
-
- local command_n = command.."\n"
-
- if os_name == "windows" then
- if file and file_bytes + #command_n >= 4096 then
- file:close()
- file = nil
- file_bytes = 0
- end
- if not file then
- file = io.open("\\\\.\\pipe\\"..options.socket, "r+b")
- end
- elseif pre_0_33_0 then
- subprocess({"/usr/bin/env", "sh", "-c", "echo '" .. command .. "' | socat - " .. options.socket})
- return
- elseif not file then
- file = io.open(options.socket, "r+")
- end
- if file then
- file_bytes = file:seek("end")
- file:write(command_n)
- file:flush()
- end
+ if not spawned then
+ return
+ end
+
+ if options.direct_io then
+ local hPipe = winapi.C.CreateFileW(
+ winapi.socket_wc,
+ winapi.GENERIC_WRITE,
+ 0,
+ nil,
+ winapi.OPEN_EXISTING,
+ winapi._createfile_pipe_flags,
+ nil
+ )
+ if hPipe ~= winapi.INVALID_HANDLE_VALUE then
+ local buf = command .. "\n"
+ winapi.C.SetNamedPipeHandleState(hPipe, winapi.PIPE_NOWAIT, nil, nil)
+ winapi.C.WriteFile(hPipe, buf, #buf + 1, winapi._lpNumberOfBytesWritten, nil)
+ winapi.C.CloseHandle(hPipe)
+ end
+
+ return
+ end
+
+ local command_n = command .. "\n"
+
+ if os_name == "windows" then
+ if file and file_bytes + #command_n >= 4096 then
+ file:close()
+ file = nil
+ file_bytes = 0
+ end
+ if not file then
+ file = io.open("\\\\.\\pipe\\" .. options.socket, "r+b")
+ end
+ elseif pre_0_33_0 then
+ subprocess({ "/usr/bin/env", "sh", "-c", "echo '" .. command .. "' | socat - " .. options.socket })
+ return
+ elseif not file then
+ file = io.open(options.socket, "r+")
+ end
+ if file then
+ file_bytes = file:seek("end")
+ file:write(command_n)
+ file:flush()
+ end
end
local function draw(w, h, script)
- if not w or not show_thumbnail then return end
- if x ~= nil then
- local scale_w, scale_h = options.scale_factor ~= 1 and (w * options.scale_factor) or nil, options.scale_factor ~= 1 and (h * options.scale_factor) or nil
- if pre_0_30_0 then
- mp.command_native({"overlay-add", options.overlay_id, x, y, options.thumbnail..".bgra", 0, "bgra", w, h, (4*w), scale_w, scale_h})
- else
- mp.command_native_async({"overlay-add", options.overlay_id, x, y, options.thumbnail..".bgra", 0, "bgra", w, h, (4*w), scale_w, scale_h}, function() end)
- end
- elseif script then
- local json, err = mp.utils.format_json({width=w, height=h, scale_factor=options.scale_factor, x=x, y=y, socket=options.socket, thumbnail=options.thumbnail, overlay_id=options.overlay_id})
- mp.commandv("script-message-to", script, "thumbfast-render", json)
- end
+ if not w or not show_thumbnail then
+ return
+ end
+ if x ~= nil then
+ local scale_w, scale_h =
+ options.scale_factor ~= 1 and (w * options.scale_factor) or nil,
+ options.scale_factor ~= 1 and (h * options.scale_factor) or nil
+ if pre_0_30_0 then
+ mp.command_native({
+ "overlay-add",
+ options.overlay_id,
+ x,
+ y,
+ options.thumbnail .. ".bgra",
+ 0,
+ "bgra",
+ w,
+ h,
+ (4 * w),
+ scale_w,
+ scale_h,
+ })
+ else
+ mp.command_native_async(
+ {
+ "overlay-add",
+ options.overlay_id,
+ x,
+ y,
+ options.thumbnail .. ".bgra",
+ 0,
+ "bgra",
+ w,
+ h,
+ (4 * w),
+ scale_w,
+ scale_h,
+ },
+ function() end
+ )
+ end
+ elseif script then
+ local json, err = mp.utils.format_json({
+ width = w,
+ height = h,
+ scale_factor = options.scale_factor,
+ x = x,
+ y = y,
+ socket = options.socket,
+ thumbnail = options.thumbnail,
+ overlay_id = options.overlay_id,
+ })
+ mp.commandv("script-message-to", script, "thumbfast-render", json)
+ end
end
local function real_res(req_w, req_h, filesize)
- local count = filesize / 4
- local diff = (req_w * req_h) - count
-
- if (properties["video-params"] and properties["video-params"]["rotate"] or 0) % 180 == 90 then
- req_w, req_h = req_h, req_w
- end
-
- if diff == 0 then
- return req_w, req_h
- else
- local threshold = 5 -- throw out results that change too much
- local long_side, short_side = req_w, req_h
- if req_h > req_w then
- long_side, short_side = req_h, req_w
- end
- for a = short_side, short_side - threshold, -1 do
- if count % a == 0 then
- local b = count / a
- if long_side - b < threshold then
- if req_h < req_w then return b, a else return a, b end
- end
- end
- end
- return nil
- end
+ local count = filesize / 4
+ local diff = (req_w * req_h) - count
+
+ if (properties["video-params"] and properties["video-params"]["rotate"] or 0) % 180 == 90 then
+ req_w, req_h = req_h, req_w
+ end
+
+ if diff == 0 then
+ return req_w, req_h
+ else
+ local threshold = 5 -- throw out results that change too much
+ local long_side, short_side = req_w, req_h
+ if req_h > req_w then
+ long_side, short_side = req_h, req_w
+ end
+ for a = short_side, short_side - threshold, -1 do
+ if count % a == 0 then
+ local b = count / a
+ if long_side - b < threshold then
+ if req_h < req_w then
+ return b, a
+ else
+ return a, b
+ end
+ end
+ end
+ end
+ return nil
+ end
end
local function move_file(from, to)
- if os_name == "windows" then
- os.remove(to)
- end
- -- move the file because it can get overwritten while overlay-add is reading it, and crash the player
- os.rename(from, to)
+ if os_name == "windows" then
+ os.remove(to)
+ end
+ -- move the file because it can get overwritten while overlay-add is reading it, and crash the player
+ os.rename(from, to)
end
local function seek(fast)
- if last_seek_time then
- run("async seek " .. last_seek_time .. (fast and " absolute+keyframes" or " absolute+exact"))
- end
+ if last_seek_time then
+ run("async seek " .. last_seek_time .. (fast and " absolute+keyframes" or " absolute+exact"))
+ end
end
-local seek_period = 3/60
+local seek_period = 3 / 60
local seek_period_counter = 0
local seek_timer
seek_timer = mp.add_periodic_timer(seek_period, function()
- if seek_period_counter == 0 then
- seek(allow_fast_seek)
- seek_period_counter = 1
- else
- if seek_period_counter == 2 then
- if allow_fast_seek then
- seek_timer:kill()
- seek()
- end
- else seek_period_counter = seek_period_counter + 1 end
- end
+ if seek_period_counter == 0 then
+ seek(allow_fast_seek)
+ seek_period_counter = 1
+ else
+ if seek_period_counter == 2 then
+ if allow_fast_seek then
+ seek_timer:kill()
+ seek()
+ end
+ else
+ seek_period_counter = seek_period_counter + 1
+ end
+ end
end)
seek_timer:kill()
local function request_seek()
- if seek_timer:is_enabled() then
- seek_period_counter = 0
- else
- seek_timer:resume()
- seek(allow_fast_seek)
- seek_period_counter = 1
- end
+ if seek_timer:is_enabled() then
+ seek_period_counter = 0
+ else
+ seek_timer:resume()
+ seek(allow_fast_seek)
+ seek_period_counter = 1
+ end
end
local function check_new_thumb()
- -- the slave might start writing to the file after checking existance and
- -- validity but before actually moving the file, so move to a temporary
- -- location before validity check to make sure everything stays consistant
- -- and valid thumbnails don't get overwritten by invalid ones
- local tmp = options.thumbnail..".tmp"
- move_file(options.thumbnail, tmp)
- local finfo = mp.utils.file_info(tmp)
- if not finfo then return false end
- spawn_waiting = false
- local w, h = real_res(effective_w, effective_h, finfo.size)
- if w then -- only accept valid thumbnails
- move_file(tmp, options.thumbnail..".bgra")
-
- real_w, real_h = w, h
- if real_w and (real_w ~= last_real_w or real_h ~= last_real_h) then
- last_real_w, last_real_h = real_w, real_h
- info(real_w, real_h)
- end
- if not show_thumbnail then
- file_timer:kill()
- end
- return true
- end
-
- return false
+ -- the slave might start writing to the file after checking existance and
+ -- validity but before actually moving the file, so move to a temporary
+ -- location before validity check to make sure everything stays consistant
+ -- and valid thumbnails don't get overwritten by invalid ones
+ local tmp = options.thumbnail .. ".tmp"
+ move_file(options.thumbnail, tmp)
+ local finfo = mp.utils.file_info(tmp)
+ if not finfo then
+ return false
+ end
+ spawn_waiting = false
+ local w, h = real_res(effective_w, effective_h, finfo.size)
+ if w then -- only accept valid thumbnails
+ move_file(tmp, options.thumbnail .. ".bgra")
+
+ real_w, real_h = w, h
+ if real_w and (real_w ~= last_real_w or real_h ~= last_real_h) then
+ last_real_w, last_real_h = real_w, real_h
+ info(real_w, real_h)
+ end
+ if not show_thumbnail then
+ file_timer:kill()
+ end
+ return true
+ end
+
+ return false
end
file_timer = mp.add_periodic_timer(file_check_period, function()
- if check_new_thumb() then
- draw(real_w, real_h, script_name)
- end
+ if check_new_thumb() then
+ draw(real_w, real_h, script_name)
+ end
end)
file_timer:kill()
local function clear()
- file_timer:kill()
- seek_timer:kill()
- if options.quit_after_inactivity > 0 then
- if show_thumbnail or activity_timer:is_enabled() then
- activity_timer:kill()
- end
- activity_timer:resume()
- end
- last_seek_time = nil
- show_thumbnail = false
- last_x = nil
- last_y = nil
- if script_name then return end
- if pre_0_30_0 then
- mp.command_native({"overlay-remove", options.overlay_id})
- else
- mp.command_native_async({"overlay-remove", options.overlay_id}, function() end)
- end
+ file_timer:kill()
+ seek_timer:kill()
+ if options.quit_after_inactivity > 0 then
+ if show_thumbnail or activity_timer:is_enabled() then
+ activity_timer:kill()
+ end
+ activity_timer:resume()
+ end
+ last_seek_time = nil
+ show_thumbnail = false
+ last_x = nil
+ last_y = nil
+ if script_name then
+ return
+ end
+ if pre_0_30_0 then
+ mp.command_native({ "overlay-remove", options.overlay_id })
+ else
+ mp.command_native_async({ "overlay-remove", options.overlay_id }, function() end)
+ end
end
local function quit()
- activity_timer:kill()
- if show_thumbnail then
- activity_timer:resume()
- return
- end
- run("quit")
- spawned = false
- real_w, real_h = nil, nil
- clear()
+ activity_timer:kill()
+ if show_thumbnail then
+ activity_timer:resume()
+ return
+ end
+ run("quit")
+ spawned = false
+ real_w, real_h = nil, nil
+ clear()
end
activity_timer = mp.add_timeout(options.quit_after_inactivity, quit)
activity_timer:kill()
local function thumb(time, r_x, r_y, script)
- if disabled then return end
-
- time = tonumber(time)
- if time == nil then return end
-
- if r_x == "" or r_y == "" then
- x, y = nil, nil
- else
- x, y = math.floor(r_x + 0.5), math.floor(r_y + 0.5)
- end
-
- script_name = script
- if last_x ~= x or last_y ~= y or not show_thumbnail then
- show_thumbnail = true
- last_x, last_y = x, y
- draw(real_w, real_h, script)
- end
-
- if options.quit_after_inactivity > 0 then
- if show_thumbnail or activity_timer:is_enabled() then
- activity_timer:kill()
- end
- activity_timer:resume()
- end
-
- if time == last_seek_time then return end
- last_seek_time = time
- if not spawned then spawn(time) end
- request_seek()
- if not file_timer:is_enabled() then file_timer:resume() end
+ if disabled then
+ return
+ end
+
+ time = tonumber(time)
+ if time == nil then
+ return
+ end
+
+ if r_x == "" or r_y == "" then
+ x, y = nil, nil
+ else
+ x, y = math.floor(r_x + 0.5), math.floor(r_y + 0.5)
+ end
+
+ script_name = script
+ if last_x ~= x or last_y ~= y or not show_thumbnail then
+ show_thumbnail = true
+ last_x, last_y = x, y
+ draw(real_w, real_h, script)
+ end
+
+ if options.quit_after_inactivity > 0 then
+ if show_thumbnail or activity_timer:is_enabled() then
+ activity_timer:kill()
+ end
+ activity_timer:resume()
+ end
+
+ if time == last_seek_time then
+ return
+ end
+ last_seek_time = time
+ if not spawned then
+ spawn(time)
+ end
+ request_seek()
+ if not file_timer:is_enabled() then
+ file_timer:resume()
+ end
end
local function watch_changes()
- if not dirty or not properties["video-out-params"] then return end
- dirty = false
-
- local old_w = effective_w
- local old_h = effective_h
-
- calc_dimensions()
-
- local vf_reset = vf_string(filters_reset)
- local rotate = properties["video-rotate"] or 0
-
- local resized = old_w ~= effective_w or
- old_h ~= effective_h or
- last_vf_reset ~= vf_reset or
- (last_rotate % 180) ~= (rotate % 180) or
- par ~= last_par or last_crop ~= properties["video-crop"]
-
- if resized then
- last_rotate = rotate
- info(effective_w, effective_h)
- elseif last_has_vid ~= has_vid and has_vid ~= 0 then
- info(effective_w, effective_h)
- end
-
- if spawned then
- if resized then
- -- mpv doesn't allow us to change output size
- local seek_time = last_seek_time
- run("quit")
- clear()
- spawned = false
- spawn(seek_time or mp.get_property_number("time-pos", 0))
- file_timer:resume()
- else
- if rotate ~= last_rotate then
- run("set video-rotate "..rotate)
- end
- local vf_runtime = vf_string(filters_runtime)
- if vf_runtime ~= last_vf_runtime then
- run("vf set "..vf_string(filters_all, true))
- last_vf_runtime = vf_runtime
- end
- end
- else
- last_vf_runtime = vf_string(filters_runtime)
- end
-
- last_vf_reset = vf_reset
- last_rotate = rotate
- last_par = par
- last_crop = properties["video-crop"]
- last_has_vid = has_vid
-
- if not spawned and not disabled and options.spawn_first and resized then
- spawn(mp.get_property_number("time-pos", 0))
- file_timer:resume()
- end
+ if not dirty or not properties["video-out-params"] then
+ return
+ end
+ dirty = false
+
+ local old_w = effective_w
+ local old_h = effective_h
+
+ calc_dimensions()
+
+ local vf_reset = vf_string(filters_reset)
+ local rotate = properties["video-rotate"] or 0
+
+ local resized = old_w ~= effective_w
+ or old_h ~= effective_h
+ or last_vf_reset ~= vf_reset
+ or (last_rotate % 180) ~= (rotate % 180)
+ or par ~= last_par
+ or last_crop ~= properties["video-crop"]
+
+ if resized then
+ last_rotate = rotate
+ info(effective_w, effective_h)
+ elseif last_has_vid ~= has_vid and has_vid ~= 0 then
+ info(effective_w, effective_h)
+ end
+
+ if spawned then
+ if resized then
+ -- mpv doesn't allow us to change output size
+ local seek_time = last_seek_time
+ run("quit")
+ clear()
+ spawned = false
+ spawn(seek_time or mp.get_property_number("time-pos", 0))
+ file_timer:resume()
+ else
+ if rotate ~= last_rotate then
+ run("set video-rotate " .. rotate)
+ end
+ local vf_runtime = vf_string(filters_runtime)
+ if vf_runtime ~= last_vf_runtime then
+ run("vf set " .. vf_string(filters_all, true))
+ last_vf_runtime = vf_runtime
+ end
+ end
+ else
+ last_vf_runtime = vf_string(filters_runtime)
+ end
+
+ last_vf_reset = vf_reset
+ last_rotate = rotate
+ last_par = par
+ last_crop = properties["video-crop"]
+ last_has_vid = has_vid
+
+ if not spawned and not disabled and options.spawn_first and resized then
+ spawn(mp.get_property_number("time-pos", 0))
+ file_timer:resume()
+ end
end
local function update_property(name, value)
- properties[name] = value
+ properties[name] = value
end
local function update_property_dirty(name, value)
- properties[name] = value
- dirty = true
- if name == "tone-mapping" then
- last_tone_mapping = nil
- end
+ properties[name] = value
+ dirty = true
+ if name == "tone-mapping" then
+ last_tone_mapping = nil
+ end
end
local function update_tracklist(name, value)
- -- current-tracks shim
- for _, track in ipairs(value) do
- if track.type == "video" and track.selected then
- properties["current-tracks/video"] = track
- return
- end
- end
+ -- current-tracks shim
+ for _, track in ipairs(value) do
+ if track.type == "video" and track.selected then
+ properties["current-tracks/video"] = track
+ return
+ end
+ end
end
local function sync_changes(prop, val)
- update_property(prop, val)
- if val == nil then return end
-
- if type(val) == "boolean" then
- if prop == "vid" then
- has_vid = 0
- last_has_vid = 0
- info(effective_w, effective_h)
- clear()
- return
- end
- val = val and "yes" or "no"
- end
-
- if prop == "vid" then
- has_vid = 1
- end
-
- if not spawned then return end
-
- run("set "..prop.." "..val)
- dirty = true
+ update_property(prop, val)
+ if val == nil then
+ return
+ end
+
+ if type(val) == "boolean" then
+ if prop == "vid" then
+ has_vid = 0
+ last_has_vid = 0
+ info(effective_w, effective_h)
+ clear()
+ return
+ end
+ val = val and "yes" or "no"
+ end
+
+ if prop == "vid" then
+ has_vid = 1
+ end
+
+ if not spawned then
+ return
+ end
+
+ run("set " .. prop .. " " .. val)
+ dirty = true
end
local function file_load()
- clear()
- spawned = false
- real_w, real_h = nil, nil
- last_real_w, last_real_h = nil, nil
- last_tone_mapping = nil
- last_seek_time = nil
- if info_timer then
- info_timer:kill()
- info_timer = nil
- end
-
- calc_dimensions()
- info(effective_w, effective_h)
+ clear()
+ spawned = false
+ real_w, real_h = nil, nil
+ last_real_w, last_real_h = nil, nil
+ last_tone_mapping = nil
+ last_seek_time = nil
+ if info_timer then
+ info_timer:kill()
+ info_timer = nil
+ end
+
+ calc_dimensions()
+ info(effective_w, effective_h)
end
local function shutdown()
- run("quit")
- remove_thumbnail_files()
- if os_name ~= "windows" then
- os.remove(options.socket)
- os.remove(options.socket..".run")
- end
+ run("quit")
+ remove_thumbnail_files()
+ if os_name ~= "windows" then
+ os.remove(options.socket)
+ os.remove(options.socket .. ".run")
+ end
end
local function on_duration(prop, val)
- allow_fast_seek = (val or 30) >= 30
+ allow_fast_seek = (val or 30) >= 30
end
mp.observe_property("current-tracks/video", "native", function(name, value)
- if pre_0_33_0 then
- mp.unobserve_property(update_tracklist)
- pre_0_33_0 = false
- end
- update_property(name, value)
+ if pre_0_33_0 then
+ mp.unobserve_property(update_tracklist)
+ pre_0_33_0 = false
+ end
+ update_property(name, value)
end)
mp.observe_property("track-list", "native", update_tracklist)