diff options
| author | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-03-14 00:07:41 +0900 |
|---|---|---|
| committer | TheSiahxyz <164138827+TheSiahxyz@users.noreply.github.com> | 2026-03-14 00:07:41 +0900 |
| commit | 5e954f3dfb4304341c1a50a1f9310f58dcf39682 (patch) | |
| tree | f4e5c1fdc9ee24b662fd9f4f43da4621a012c29a /ar/.config/mpv/scripts/thumbfast.lua | |
| parent | a13bf1ecb918bcf80ecb8bb9663ac7a84d576243 (diff) | |
updates
Diffstat (limited to 'ar/.config/mpv/scripts/thumbfast.lua')
| -rw-r--r-- | ar/.config/mpv/scripts/thumbfast.lua | 1603 |
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) |
