Files
mpv-config/scripts/uosc_danmaku/main.lua
T
2026-03-27 07:06:16 +01:00

930 lines
29 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
VERSION = "2.0.0"
mp.commandv('script-message', 'uosc_danmaku-version', VERSION)
local msg = require('mp.msg')
local utils = require("mp.utils")
AES = require("modules/aes")
Base64 = require("modules/base64")
MD5 = require("modules/md5")
Sha256 = require("modules/hash")
require("modules/options")
require("modules/utils")
require("modules/parse")
require("modules/guess")
require('modules/render')
require('modules/menu')
require("modules/update")
require("apis/dandanplay")
require('apis/extra')
DANMAKU_PATH = os.getenv("TEMP") or "/tmp/"
HISTORY_PATH = mp.command_native({"expand-path", options.history_path})
PID = utils.getpid()
DANMAKU = {sources = {}, count = 1}
DELAYS = {}
ENABLED, COMMENTS, DELAY = false, nil, 0
DELAY_PROPERTY = string.format("user-data/%s/danmaku-delay", mp.get_script_name())
mp.set_property_native(DELAY_PROPERTY, 0)
KEY = table_to_zero_indexed({
0x00,0x01,0x02,0x03,0x04,
0x05,0x06,0x07,0x08,0x09,
0x0a,0x0b,0x0c,0x0d,0x0e,
0x0f,0x10,0x11,0x12,0x13,
0x14,0x15,0x16,0x17,0x18,
0x19,0x1a,0x1b,0x1c,0x1d,
0x1e,0x1f
})
PLATFORM = (function()
local platform = mp.get_property_native("platform")
if platform then
if itable_index_of({ "windows", "darwin" }, platform) then
return platform
end
else
if os.getenv("windir") ~= nil then
return "windows"
end
local homedir = os.getenv("HOME")
if homedir ~= nil and string.sub(homedir, 1, 6) == "/Users" then
return "darwin"
end
end
return "linux"
end)()
function get_danmaku_visibility()
local history_json = read_file(HISTORY_PATH)
local history
if history_json ~= nil then
history = utils.parse_json(history_json) or {}
local flag = history["show_danmaku"]
if flag == nil then
history["show_danmaku"] = false
write_json_file(HISTORY_PATH, history)
else
return flag
end
else
history = {}
history["show_danmaku"] = false
write_json_file(HISTORY_PATH, history)
end
return false
end
function set_danmaku_visibility(flag)
local history = {}
local history_json = read_file(HISTORY_PATH)
if history_json ~= nil then
history = utils.parse_json(history_json) or {}
end
history["show_danmaku"] = flag
write_json_file(HISTORY_PATH, history)
end
function set_danmaku_button()
if get_danmaku_visibility() then
mp.commandv("script-message-to", "uosc", "set", "show_danmaku", "on")
end
end
function show_loaded(init)
if DANMAKU.anime and DANMAKU.episode then
show_message("匹配内容:" .. DANMAKU.anime .. "-" .. DANMAKU.episode .. "\\N弹幕加载成功共计" .. #COMMENTS .. "条弹幕", 3)
if init then
msg.info(DANMAKU.anime .. "-" .. DANMAKU.episode .. " 弹幕加载成功,共计" .. #COMMENTS .. "条弹幕")
end
else
show_message("弹幕加载成功,共计" .. #COMMENTS .. "条弹幕", 3)
end
end
local function get_cid()
local cid, danmaku_id = nil, nil
local tracks = mp.get_property_native("track-list")
for _, track in ipairs(tracks) do
if track["lang"] == "danmaku" then
cid = track["external-filename"]:match("/(%d-)%.xml$")
danmaku_id = track["id"]
break
end
end
return cid, danmaku_id
end
local function extract_between_colons(input_string)
local start_index = 0
local end_index = 0
local count = 0
for i = 1, #input_string do
if input_string:sub(i, i) == ":" then
count = count + 1
if count == 2 then
start_index = i
elseif count == 3 then
end_index = i
break
end
end
end
if start_index > 0 and end_index > 0 then
return input_string:sub(start_index + 1, end_index - 1)
else
return nil
end
end
local function hex_to_int_color(hex_color)
-- 移除颜色代码中的'#'字符
hex_color = hex_color:sub(2) -- 只保留颜色代码部分
-- 提取R, G, B的十六进制值并转为整数
local r = tonumber(hex_color:sub(1, 2), 16)
local g = tonumber(hex_color:sub(3, 4), 16)
local b = tonumber(hex_color:sub(5, 6), 16)
-- 计算32位整数值
local color_int = (r * 256 * 256) + (g * 256) + b
return color_int
end
local function get_type_from_position(position)
if position == 0 then
return 1
end
if position == 1 then
return 4
end
return 5
end
-- 获取指定时间的延迟
-- 返回该时间点之前所有延迟段的总和
function get_delay_for_time(delay_segments, time)
if not delay_segments or #delay_segments == 0 then return 0 end
table.sort(delay_segments, function(a, b) return a.start < b.start end)
local applied_delay = 0
for i = 1, #delay_segments do
local seg = delay_segments[i]
local delay = tonumber(seg.delay)
if time >= seg.start and delay then
applied_delay = applied_delay + delay
else
break
end
end
return applied_delay
end
local function merge_delay_segments(segments)
if not segments or #segments == 0 then return {} end
local NEAREST_THRESHOLD = 10 -- 最邻近段合并阈值
local MERGE_THRESHOLD = 30 -- 跨段合并阈值
local EPSILON = 1e-6 -- 判断接近 0 的阈值
table.sort(segments, function(a, b) return a.start < b.start end)
local partially_merged = {}
local i = 1
while i <= #segments do
local cur = segments[i]
local next_seg = segments[i + 1]
if next_seg and (next_seg.start - cur.start) <= NEAREST_THRESHOLD then
local combined_delay = tonumber(cur.delay) + tonumber(next_seg.delay)
if math.abs(combined_delay) > EPSILON then
table.insert(partially_merged, {
start = cur.start,
delay = combined_delay
})
end
i = i + 2
else
if math.abs(tonumber(cur.delay)) > EPSILON then
table.insert(partially_merged, cur)
end
i = i + 1
end
end
local merged = {}
for _, seg in ipairs(partially_merged) do
local merged_flag = false
for idx, m in ipairs(merged) do
if math.abs(seg.start - m.start) <= MERGE_THRESHOLD then
m.delay = tonumber(m.delay) + tonumber(seg.delay)
if math.abs(m.delay) <= EPSILON then
table.remove(merged, idx)
end
merged_flag = true
break
end
end
if not merged_flag then
if math.abs(tonumber(seg.delay)) > EPSILON then
table.insert(merged, {
start = seg.start,
delay = seg.delay
})
end
end
end
table.sort(merged, function(a, b) return a.start < b.start end)
return merged
end
local function set_danmaku_delay(dly, time)
for url, source in pairs(DANMAKU.sources) do
if source.fname and not source.blocked then
source.delay_segments = source.delay_segments or {}
if dly == 0 then
source.delay_segments = {}
elseif time then
table.insert(source.delay_segments, {start = time, delay = dly})
else
table.insert(source.delay_segments, {start = 0, delay = dly})
end
source.delay = nil
table.sort(source.delay_segments, function(a, b) return a.start < b.start end)
add_source_to_history(url, source)
end
end
if time then
table.insert(DELAYS, {start = time, delay = dly})
else
table.insert(DELAYS, {start = 0, delay = dly})
end
if dly == 0 then
DELAY = 0
DELAYS = {}
else
DELAY = DELAY + dly
end
DELAYS = merge_delay_segments(DELAYS)
if ENABLED and COMMENTS ~= nil then
render()
end
show_message('设置弹幕延迟: ' .. string.format("%.1f", DELAY + 1e-10) .. ' s')
mp.set_property_native(DELAY_PROPERTY, DELAY)
end
local function clear_source()
local path = mp.get_property("path")
local history_json = read_file(HISTORY_PATH)
if not path or not history_json then return end
local history = utils.parse_json(history_json) or {}
if history[path] == nil then return end
history[path] = nil
write_json_file(HISTORY_PATH, history)
for url, source in pairs(DANMAKU.sources) do
if source.from == "user_custom" then
if source.fname and file_exists(source.fname) then
os.remove(source.fname)
end
DANMAKU.sources[url] = nil
end
end
load_danmaku(false)
show_message("已重置当前视频所有弹幕源更改", 3)
msg.verbose("已重置当前视频所有弹幕源更改")
end
function write_history(episodeid)
local history = {}
local path = mp.get_property("path")
local dir = get_parent_directory(path)
local fname = mp.get_property('filename/no-ext')
local episodeNumber = 0
if episodeid then
episodeNumber = tonumber(episodeid) % 1000
elseif DANMAKU.extra then
episodeNumber = DANMAKU.extra.episodenum
end
if is_protocol(path) then
local title, season_num, episod_num = parse_title()
if title and episod_num then
if season_num then
dir = title .." Season".. season_num
else
dir = title
end
fname = url_decode(mp.get_property("media-title"))
episodeNumber = episod_num
end
end
if dir ~= nil then
local history_json = read_file(HISTORY_PATH)
if history_json ~= nil then
history = utils.parse_json(history_json) or {}
end
history[dir] = {}
history[dir].fname = fname
history[dir].source = DANMAKU.source
history[dir].animeTitle = DANMAKU.anime
history[dir].episodeTitle = DANMAKU.episode
history[dir].episodeNumber = episodeNumber
if episodeid then
history[dir].episodeId = episodeid
elseif DANMAKU.extra then
history[dir].extra = DANMAKU.extra
end
write_json_file(HISTORY_PATH, history)
end
end
function remove_source_from_history(rm_source)
local history_json = read_file(HISTORY_PATH)
local path = mp.get_property("path")
if is_protocol(path) then
path = remove_query(path)
end
if history_json then
local history = utils.parse_json(history_json) or {}
if history[path] ~= nil and history[path]["sources"] ~= nil then
for source in pairs(history[path]["sources"]) do
if source == rm_source then
history[path]["sources"][source] = nil
break
end
end
end
write_json_file(HISTORY_PATH, history)
end
end
function add_source_to_history(add_url, add_source)
local history_json = read_file(HISTORY_PATH)
local path = mp.get_property("path")
if is_protocol(path) then
path = remove_query(path)
end
local history = {}
if history_json then
history = utils.parse_json(history_json) or {}
end
history[path] = history[path] or {}
history[path]["sources"] = history[path]["sources"] or {}
history[path]["sources"][add_url] = history[path]["sources"][add_url] or {}
local record = history[path]["sources"][add_url]
record.from = add_source.from or "user_custom"
record.blocked = add_source.blocked or false
local delay_segments = shallow_copy(add_source.delay_segments or {})
if #delay_segments > 0 then
record.delay_segments = merge_delay_segments(delay_segments)
if #record.delay_segments == 0 then
record.delay_segments = nil
end
else
record.delay_segments = nil
end
record.delay = nil
write_json_file(HISTORY_PATH, history)
end
function read_danmaku_source_record(path)
if is_protocol(path) then
path = remove_query(path)
end
local history_json = read_file(HISTORY_PATH)
if not history_json then return end
local history = utils.parse_json(history_json) or {}
local record = history[path]
if not record or not record.sources then return end
local sources = record.sources
local upgraded_sources = {}
if is_nested_table(sources) then
for source, data in pairs(sources) do
local from = data.from or "user_custom"
local blocked = data.blocked or false
local delay_segments = shallow_copy(data.delay_segments or {})
if data.delay ~= nil then
for i = #delay_segments, 1, -1 do
if delay_segments[i].start == 0 then
table.remove(delay_segments, i)
end
end
table.insert(delay_segments, 1, { start = 0, delay = tonumber(data.delay) })
end
if #delay_segments > 0 then
delay_segments = merge_delay_segments(delay_segments)
else
delay_segments = nil
end
DANMAKU.sources[source] = {
from = from,
blocked = blocked,
delay_segments = delay_segments,
from_history = true,
}
end
else
for _, raw in ipairs(sources) do
local source = raw
local blocked = false
local from = raw:match("<(.-)>")
local delay = raw:match("{{(.-)}}")
source = source:gsub("<.->", ""):gsub("{{.-}}", "")
if source:match("^%-") then
source = source:sub(2)
blocked = true
from = from or "api_server"
end
local delay_segments = nil
if delay ~= nil then
delay_segments = {
{ start = 0, delay = tonumber(delay) }
}
end
DANMAKU.sources[source] = {
from = from or "user_custom",
blocked = blocked,
delay_segments = delay_segments,
from_history = true,
}
upgraded_sources[source] = shallow_copy(DANMAKU.sources[source])
end
if next(upgraded_sources) then
record.sources = upgraded_sources
write_json_file(HISTORY_PATH, history)
end
end
end
-- 收集现有的弹幕文件和延迟记录
local function collect_danmaku_sources()
local danmaku_input = {}
local delays = {}
for _, source in pairs(DANMAKU.sources) do
if not source.blocked and source.fname then
if not file_exists(source.fname) then
show_message("未找到弹幕文件", 3)
msg.info("未找到弹幕文件")
return
end
table.insert(danmaku_input, source.fname)
if source.delay_segments and #source.delay_segments > 0 then
table.insert(delays, source.delay_segments)
end
end
end
return danmaku_input, delays
end
-- 视频播放时保存弹幕
function save_danmaku(not_forced)
local danmaku_input, delays = collect_danmaku_sources()
if #danmaku_input == 0 then
show_message("弹幕内容为空,无法保存", 3)
msg.verbose("弹幕内容为空,无法保存")
COMMENTS = {}
return
end
local path = mp.get_property("path")
local dir = get_parent_directory(path) or ""
local filename = mp.get_property('filename/no-ext')
local danmaku_out = utils.join_path(dir, filename .. ".xml")
-- 排除网络播放场景
if not path or is_protocol(path) or (not file_exists(danmaku_out)
and not is_writable(danmaku_out)) then
show_message("此弹幕文件不支持保存至本地")
msg.warn("此弹幕文件不支持保存至本地")
else
if not_forced and file_exists(danmaku_out) then
show_message("已存在同名弹幕文件:" .. danmaku_out)
msg.info("已存在同名弹幕文件:" .. danmaku_out)
return
else
convert_danmaku_to_xml(danmaku_input, danmaku_out, delays)
end
end
end
-- 加载弹幕
function load_danmaku(from_menu, no_osd)
if not ENABLED then return end
local temp_file = "danmaku-" .. PID .. ".ass"
local danmaku_file = utils.join_path(DANMAKU_PATH, temp_file)
local danmaku_input, delays = collect_danmaku_sources()
-- 如果没有弹幕文件,退出加载
if #danmaku_input == 0 then
show_message("该集弹幕内容为空,结束加载", 3)
msg.verbose("该集弹幕内容为空,结束加载")
COMMENTS = {}
return
end
convert_danmaku_format(danmaku_input, danmaku_file, delays)
parse_danmaku(danmaku_file, from_menu, no_osd)
end
-- 为 bilibli 网站的视频播放加载弹幕
function load_danmaku_for_bilibili(path)
local cid, danmaku_id = get_cid()
if danmaku_id ~= nil then
mp.commandv('sub-remove', danmaku_id)
end
if cid == nil then
cid = mp.get_opt('cid')
if not cid then
local patterns = {
"bilivideo%.c[nom]+.*/resource/(%d+)%D+.*",
"bilivideo%.c[nom]+.*/(%d+)-%d+-%d+%..*%?",
}
local urls = {
path,
mp.get_property("stream-open-filename", ''),
}
for _, pattern in ipairs(patterns) do
for _, url in ipairs(urls) do
if url:find(pattern) then
cid = url:match(pattern)
break
end
end
end
end
end
if cid == nil and path:match("/video/BV.-") then
if path:match("video/BV.-/.*") then
path = path:gsub("/[^/]+$", "")
end
add_danmaku_source_online(path, true)
return
end
if cid ~= nil then
local url = "https://comment.bilibili.com/" .. cid .. ".xml"
local temp_file = "danmaku-" .. PID .. DANMAKU.count .. ".xml"
local danmaku_xml = utils.join_path(DANMAKU_PATH, temp_file)
DANMAKU.count = DANMAKU.count + 1
local arg = {
"curl",
"-L",
"-s",
"--compressed",
"--user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36 Edg/124.0.0.0",
"--output",
danmaku_xml,
url,
}
call_cmd_async(arg, function(error)
async_running = false
if error then
show_message("HTTP 请求失败,打开控制台查看详情", 5)
msg.error(error)
return
end
if file_exists(danmaku_xml) then
save_danmaku_downloaded(path, danmaku_xml)
load_danmaku(true)
end
end)
end
end
-- 为 bahamut 网站的视频播放加载弹幕
function load_danmaku_for_bahamut(path)
local path = path:gsub('%%(%x%x)', hex_to_char)
local sn = extract_between_colons(path)
if sn == nil then
return
end
local url = "https://ani.gamer.com.tw/ajax/danmuGet.php"
local temp_file = "bahamut-" .. PID .. ".json"
local danmaku_json = utils.join_path(DANMAKU_PATH, temp_file)
local arg = {
"curl",
"-X",
"POST",
"-d",
"sn=" .. sn,
"-L",
"-s",
"--user-agent",
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.83 Safari/537.36",
"--header",
"Origin: https://ani.gamer.com.tw",
"--header",
"Content-Type: application/x-www-form-urlencoded;charset=utf-8",
"--header",
"Accept: application/json",
"--header",
"Authority: ani.gamer.com.tw",
"--output",
danmaku_json,
url,
}
if options.proxy ~= "" then
table.insert(arg, '-x')
table.insert(arg, options.proxy)
end
call_cmd_async(arg, function(error)
async_running = false
if error then
show_message("HTTP 请求失败,打开控制台查看详情", 5)
msg.error(error)
return
end
if not file_exists(danmaku_json) then
url = "https://ani.gamer.com.tw/animeVideo.php?sn=" .. sn
ENABLED = true
add_danmaku_source_online(url, true)
return
end
local comments_json = read_file(danmaku_json)
local comments = utils.parse_json(comments_json)
if not comments then
return
end
temp_file = "danmaku-" .. PID .. DANMAKU.count .. ".json"
local json_filename = utils.join_path(DANMAKU_PATH, temp_file)
DANMAKU.count = DANMAKU.count + 1
local json_file = io.open(json_filename, "w")
if json_file then
json_file:write("[\n")
for _, comment in ipairs(comments) do
local m = comment["text"]
local color = hex_to_int_color(comment["color"])
local mode = get_type_from_position(comment["position"])
local time = tonumber(comment["time"]) / 10
local c = time .. "," .. color .. "," .. mode .. ",25,,,"
-- Write the JSON object as a single line, no spaces or extra formatting
local json_entry = string.format('{"c":"%s","m":"%s"},\n', c, m)
json_file:write(json_entry)
end
json_file:write("]")
json_file:close()
end
if file_exists(json_filename) then
save_danmaku_downloaded(
"https://ani.gamer.com.tw/animeVideo.php?sn=" .. sn,
json_filename)
load_danmaku(true)
end
end)
end
function load_danmaku_for_url(path)
if path:find('bilibili.com') or path:find('bilivideo.c[nom]+') then
load_danmaku_for_bilibili(path)
return
end
if path:find('bahamut.akamaized.net') then
load_danmaku_for_bahamut(path)
return
end
local title, season_num, episod_num = parse_title()
local filename = url_decode(mp.get_property("media-title"))
local episod_number = nil
if title and episod_num then
if season_num then
dir = title .." Season".. season_num
episod_number = episod_num
else
dir = title
end
auto_load_danmaku(path, dir, filename, episod_number)
addon_danmaku(dir, false)
return
end
get_danmaku_with_hash(filename, path)
addon_danmaku()
end
-- 自动加载上次匹配的弹幕
function auto_load_danmaku(path, dir, filename, number)
if dir ~= nil then
local history_json = read_file(HISTORY_PATH)
if history_json ~= nil then
local history = utils.parse_json(history_json) or {}
-- 1.判断父文件名是否存在
local history_dir = history[dir]
if history_dir ~= nil then
--2.如果存在则获取number和id
DANMAKU.anime = history_dir.animeTitle
local episode_number = history_dir.episodeTitle and history_dir.episodeTitle:match("%d+")
local history_number = history_dir.episodeNumber
local history_id = history_dir.episodeId
local history_fname = history_dir.fname
local history_extra = history_dir.extra
local playing_number = nil
if history_fname then
if filename ~= history_fname then
if number then
playing_number = number
else
history_number, playing_number = get_episode_number(filename, history_fname)
end
else
playing_number = history_number
end
else
playing_number = get_episode_number(filename)
end
if playing_number ~= nil then
local x = playing_number - history_number --获取集数差值
DANMAKU.episode = episode_number and string.format("第%s话", episode_number + x) or history_dir.episodeTitle
show_message("自动加载上次匹配的弹幕", 3)
msg.verbose("自动加载上次匹配的弹幕")
if history_id then
local tmp_id = tostring(x + history_id)
set_episode_id(tmp_id)
elseif history_extra then
local episodenum = history_extra.episodenum + x
get_details(history_extra.class, history_extra.id, history_extra.site,
history_extra.title, history_extra.year, history_extra.number, episodenum)
end
else
get_danmaku_with_hash(filename, path)
end
else
get_danmaku_with_hash(filename, path)
end
else
get_danmaku_with_hash(filename, path)
end
end
end
function init(path)
if not path then return end
local dir = get_parent_directory(path)
local filename = mp.get_property('filename/no-ext')
local video = mp.get_property_native("current-tracks/video")
local duration = mp.get_property_number("duration", 0)
if not video or video["image"] or video["albumart"] or duration < 60 then
msg.info("不支持的播放内容(非视频)")
return
end
if is_protocol(path) then
load_danmaku_for_url(path)
end
if dir and filename then
local danmaku_xml = utils.join_path(dir, filename .. ".xml")
if file_exists(danmaku_xml) then
add_danmaku_source_local(danmaku_xml, true)
else
auto_load_danmaku(path, dir, filename)
addon_danmaku(dir, true)
end
end
end
mp.register_event("file-loaded", function()
local path = mp.get_property("path")
local dir = get_parent_directory(path)
local filename = mp.get_property('filename/no-ext')
local video = mp.get_property_native("current-tracks/video")
local fps = mp.get_property_number("container-fps", 0)
local duration = mp.get_property_number("duration", 0)
if not video or video["image"] or video["albumart"] or fps < 23 or duration < 60 then
return
end
read_danmaku_source_record(path)
if not get_danmaku_visibility() then
return
end
if options.autoload_for_url and is_protocol(path) then
ENABLED = true
load_danmaku_for_url(path)
end
if filename == nil or dir == nil then
return
end
local danmaku_xml = utils.join_path(dir, filename .. ".xml")
if options.autoload_local_danmaku then
if file_exists(danmaku_xml) then
ENABLED = true
add_danmaku_source_local(danmaku_xml)
return
end
end
if options.auto_load then
ENABLED = true
auto_load_danmaku(path, dir, filename)
addon_danmaku(dir, false)
return
end
if ENABLED and COMMENTS == nil and not async_running then
init(path)
end
end)
-------------- 键位绑定 --------------
mp.add_key_binding(options.open_search_danmaku_menu_key, "open_search_danmaku_menu", function()
mp.commandv("script-message", "open_search_danmaku_menu")
end)
mp.add_key_binding(options.show_danmaku_keyboard_key, "show_danmaku_keyboard", function()
mp.commandv("script-message", "show_danmaku_keyboard")
end)
mp.register_script_message("danmaku-delay", function(...)
local commands = {...}
local delay_str, time_str = commands[1], commands[2]
local dly = tonumber(delay_str)
local time = time_str and tonumber(time_str)
if type(dly) ~= "number" then
show_message("参数错误:缺少有效的延迟秒数", 3)
return
end
set_danmaku_delay(dly, time)
end)
mp.register_script_message("show_danmaku_keyboard", function()
ENABLED = not ENABLED
if ENABLED then
mp.commandv("script-message-to", "uosc", "set", "show_danmaku", "on")
set_danmaku_visibility(true)
if COMMENTS == nil then
show_message("加载弹幕初始化...", 3)
local path = mp.get_property("path")
init(path)
else
show_loaded()
show_danmaku_func()
end
else
show_message("关闭弹幕", 2)
mp.commandv("script-message-to", "uosc", "set", "show_danmaku", "off")
set_danmaku_visibility(false)
hide_danmaku_func()
end
end)
mp.register_script_message("check-update", check_for_update)
mp.register_script_message("clear-source", clear_source)
mp.register_script_message("immediately_save_danmaku", save_danmaku)
mp.register_script_message("open_source_delay_menu", danmaku_delay_setup)
mp.register_script_message("open_search_danmaku_menu", open_input_menu)
mp.register_script_message("open_add_source_menu", open_add_menu)
mp.register_script_message("open_add_total_menu", open_add_total_menu)