mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-27 18:22:41 -08:00
389 lines
8.8 KiB
Lua
389 lines
8.8 KiB
Lua
local function run_plugin_scenario(config)
|
|
config = config or {}
|
|
|
|
local recorded = {
|
|
async_calls = {},
|
|
sync_calls = {},
|
|
script_messages = {},
|
|
events = {},
|
|
osd = {},
|
|
logs = {},
|
|
}
|
|
|
|
local function make_mp_stub()
|
|
local mp = {}
|
|
|
|
function mp.get_property(name)
|
|
if name == "platform" then
|
|
return config.platform or "linux"
|
|
end
|
|
if name == "filename/no-ext" then
|
|
return config.filename_no_ext or ""
|
|
end
|
|
if name == "filename" then
|
|
return config.filename or ""
|
|
end
|
|
if name == "path" then
|
|
return config.path or ""
|
|
end
|
|
if name == "media-title" then
|
|
return config.media_title or ""
|
|
end
|
|
return ""
|
|
end
|
|
|
|
function mp.get_property_native(_name)
|
|
return config.chapter_list or {}
|
|
end
|
|
|
|
function mp.command_native(command)
|
|
recorded.sync_calls[#recorded.sync_calls + 1] = command
|
|
local args = command.args or {}
|
|
if args[1] == "ps" then
|
|
return {
|
|
status = 0,
|
|
stdout = config.process_list or "",
|
|
stderr = "",
|
|
}
|
|
end
|
|
if args[1] == "curl" then
|
|
return { status = 0, stdout = "{}", stderr = "" }
|
|
end
|
|
return { status = 0, stdout = "", stderr = "" }
|
|
end
|
|
|
|
function mp.command_native_async(command, callback)
|
|
recorded.async_calls[#recorded.async_calls + 1] = command
|
|
if callback then
|
|
callback(true, { status = 0, stdout = "", stderr = "" }, nil)
|
|
end
|
|
end
|
|
|
|
function mp.add_timeout(_seconds, callback)
|
|
if callback then
|
|
callback()
|
|
end
|
|
end
|
|
|
|
function mp.register_script_message(name, fn)
|
|
recorded.script_messages[name] = fn
|
|
end
|
|
|
|
function mp.add_key_binding(_keys, _name, _fn) end
|
|
function mp.register_event(name, fn)
|
|
if not recorded.events[name] then
|
|
recorded.events[name] = {}
|
|
end
|
|
recorded.events[name][#recorded.events[name] + 1] = fn
|
|
end
|
|
function mp.add_hook(_name, _prio, _fn) end
|
|
function mp.observe_property(_name, _kind, _fn) end
|
|
function mp.osd_message(message, _duration)
|
|
recorded.osd[#recorded.osd + 1] = message
|
|
end
|
|
function mp.get_time()
|
|
return 0
|
|
end
|
|
function mp.commandv(...) end
|
|
function mp.set_property_native(...) end
|
|
function mp.set_property(...) end
|
|
function mp.set_osd_ass(...) end
|
|
function mp.get_property_number(_name, default)
|
|
return default
|
|
end
|
|
function mp.get_property_bool(_name, default)
|
|
return default
|
|
end
|
|
function mp.get_script_name()
|
|
return "subminer"
|
|
end
|
|
function mp.get_script_directory()
|
|
return "plugin/subminer"
|
|
end
|
|
|
|
return mp
|
|
end
|
|
|
|
local mp = make_mp_stub()
|
|
local options = {}
|
|
local utils = {}
|
|
|
|
function options.read_options(target, _name)
|
|
if config.socket_path then
|
|
target.socket_path = config.socket_path
|
|
end
|
|
if config.binary_path then
|
|
target.binary_path = config.binary_path
|
|
end
|
|
end
|
|
|
|
function utils.file_info(path)
|
|
local exists = config.files and config.files[path]
|
|
if exists then
|
|
return { is_dir = false }
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function utils.join_path(...)
|
|
local parts = { ... }
|
|
return table.concat(parts, "/")
|
|
end
|
|
|
|
function utils.parse_json(_json)
|
|
return {}, nil
|
|
end
|
|
|
|
package.loaded["mp"] = nil
|
|
package.loaded["mp.input"] = nil
|
|
package.loaded["mp.msg"] = nil
|
|
package.loaded["mp.options"] = nil
|
|
package.loaded["mp.utils"] = nil
|
|
for key, _ in pairs(package.loaded) do
|
|
if key:match("^subminer") then
|
|
package.loaded[key] = nil
|
|
end
|
|
end
|
|
local plugin_modules = {
|
|
"init",
|
|
"bootstrap",
|
|
"options",
|
|
"state",
|
|
"log",
|
|
"binary",
|
|
"environment",
|
|
"process",
|
|
"aniskip",
|
|
"aniskip_match",
|
|
"hover",
|
|
"ui",
|
|
"messages",
|
|
"lifecycle",
|
|
}
|
|
for _, module_name in ipairs(plugin_modules) do
|
|
package.loaded[module_name] = nil
|
|
end
|
|
|
|
package.preload["mp"] = function()
|
|
return mp
|
|
end
|
|
package.preload["mp.input"] = function()
|
|
return {
|
|
select = function(_) end,
|
|
}
|
|
end
|
|
package.preload["mp.msg"] = function()
|
|
return {
|
|
info = function(line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
warn = function(line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
error = function(line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
debug = function(line)
|
|
recorded.logs[#recorded.logs + 1] = line
|
|
end,
|
|
}
|
|
end
|
|
package.preload["mp.options"] = function()
|
|
return options
|
|
end
|
|
package.preload["mp.utils"] = function()
|
|
return utils
|
|
end
|
|
|
|
local ok, err = pcall(dofile, "plugin/subminer/main.lua")
|
|
if not ok then
|
|
return nil, err, recorded
|
|
end
|
|
if config.trigger_file_loaded and recorded.events["file-loaded"] then
|
|
for _, callback in ipairs(recorded.events["file-loaded"]) do
|
|
callback()
|
|
end
|
|
end
|
|
return recorded, nil, recorded
|
|
end
|
|
|
|
local function assert_true(condition, message)
|
|
if condition then
|
|
return
|
|
end
|
|
error(message)
|
|
end
|
|
|
|
local function find_start_call(async_calls)
|
|
for _, call in ipairs(async_calls) do
|
|
local args = call.args or {}
|
|
for i = 1, #args do
|
|
if args[i] == "--start" then
|
|
return call
|
|
end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function has_command_flag(calls, flag)
|
|
for _, call in ipairs(calls) do
|
|
local args = call.args or {}
|
|
for i = 1, #args do
|
|
if args[i] == flag then
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function has_sync_command(sync_calls, executable)
|
|
for _, call in ipairs(sync_calls) do
|
|
local args = call.args or {}
|
|
if args[1] == executable then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function make_hover_context(config)
|
|
local state = require("state").new()
|
|
local captured = {
|
|
osd_ass = nil,
|
|
}
|
|
local mp = {}
|
|
|
|
function mp.get_property(name)
|
|
if name == "sub-text/ass" then
|
|
return config.ass_text or ""
|
|
end
|
|
if name == "sub-text-ass" then
|
|
return ""
|
|
end
|
|
if name == "sub-color" then
|
|
return config.sub_color
|
|
end
|
|
if name == "sub-font" then
|
|
return "sans-serif"
|
|
end
|
|
if name == "sub-visibility" or name == "secondary-sub-visibility" then
|
|
return "yes"
|
|
end
|
|
return ""
|
|
end
|
|
|
|
function mp.get_property_number(_name, default)
|
|
return default
|
|
end
|
|
|
|
function mp.get_property_bool(_name, default)
|
|
return default
|
|
end
|
|
|
|
function mp.get_property_native(name)
|
|
if name == "osd-dimensions" then
|
|
return { w = 1280, h = 720, ml = 0, mr = 0, mt = 0, mb = 0 }
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function mp.set_property(_name, _value) end
|
|
|
|
function mp.set_osd_ass(_w, _h, text)
|
|
captured.osd_ass = text
|
|
end
|
|
|
|
function mp.get_time()
|
|
return 0
|
|
end
|
|
|
|
function mp.add_timeout(_seconds, callback)
|
|
if callback then
|
|
callback()
|
|
end
|
|
return {
|
|
kill = function() end,
|
|
}
|
|
end
|
|
|
|
return {
|
|
ctx = {
|
|
mp = mp,
|
|
msg = { warn = function(_) end },
|
|
utils = {
|
|
parse_json = function(_)
|
|
return {
|
|
revision = 1,
|
|
hoveredTokenIndex = 0,
|
|
subtitle = "hello world",
|
|
tokens = {
|
|
{ index = 0, text = "hello", startPos = 0, endPos = 5 },
|
|
},
|
|
}, nil
|
|
end,
|
|
},
|
|
state = state,
|
|
},
|
|
captured = captured,
|
|
}
|
|
end
|
|
|
|
local binary_path = "/tmp/subminer-binary"
|
|
|
|
do
|
|
local recorded, err = run_plugin_scenario({
|
|
process_list = "",
|
|
binary_path = binary_path,
|
|
files = {
|
|
[binary_path] = true,
|
|
},
|
|
})
|
|
assert_true(recorded ~= nil, "plugin failed to load for cold-start scenario: " .. tostring(err))
|
|
assert_true(recorded.script_messages["subminer-start"] ~= nil, "subminer-start script message not registered")
|
|
assert_true(recorded.script_messages["subminer-stop"] ~= nil, "subminer-stop script message not registered")
|
|
recorded.script_messages["subminer-start"]("texthooker=no")
|
|
assert_true(find_start_call(recorded.async_calls) ~= nil, "expected cold-start to invoke --start command when process is absent")
|
|
recorded.script_messages["subminer-stop"]()
|
|
assert_true(has_command_flag(recorded.sync_calls, "--stop"), "expected stop message to invoke --stop command")
|
|
assert_true(
|
|
not has_sync_command(recorded.sync_calls, "ps"),
|
|
"expected cold-start start command to avoid synchronous process list scan"
|
|
)
|
|
end
|
|
|
|
do
|
|
local recorded, err = run_plugin_scenario({
|
|
process_list = "python\nSubMiner\n",
|
|
filename_no_ext = "Some Show - S01E01",
|
|
trigger_file_loaded = true,
|
|
binary_path = binary_path,
|
|
files = {
|
|
[binary_path] = true,
|
|
},
|
|
})
|
|
assert_true(recorded ~= nil, "plugin failed to load for process split scenario: " .. tostring(err))
|
|
assert_true(has_sync_command(recorded.sync_calls, "ps"), "expected file-loaded hook to read process list")
|
|
assert_true(
|
|
has_sync_command(recorded.sync_calls, "curl"),
|
|
"expected file-loaded hook to run AniSkip lookup when SubMiner process is present in ps output"
|
|
)
|
|
end
|
|
|
|
do
|
|
local hover_context = make_hover_context({
|
|
ass_text = "hello world",
|
|
sub_color = "112233",
|
|
})
|
|
local hover = require("hover").create(hover_context.ctx)
|
|
hover.handle_hover_message("{}")
|
|
assert_true(type(hover_context.captured.osd_ass) == "string", "expected hover overlay render to write ASS output")
|
|
assert_true(
|
|
hover_context.captured.osd_ass:find("\\1c&HF6A0C6&", 1, true) ~= nil,
|
|
"expected hover render to keep accent hover color when sub-color is configured"
|
|
)
|
|
end
|
|
|
|
print("plugin start gate regression tests: OK")
|