mirror of
https://github.com/rydesun/dotfiles.git
synced 2025-12-26 14:44:58 +08:00
238 lines
7.5 KiB
Lua
238 lines
7.5 KiB
Lua
local mp = require 'mp'
|
|
local utils = require 'mp.utils'
|
|
|
|
local function get_xattr_comment(path)
|
|
local p = io.popen(string.format(
|
|
"getfattr -n user.timemarks --only-values %q 2>/dev/null", path
|
|
))
|
|
if not p then return nil end
|
|
local out = p:read("*a")
|
|
p:close()
|
|
return #out > 0 and out or nil
|
|
end
|
|
|
|
local function set_xattr_comment(path, comment)
|
|
local cmd
|
|
if comment == "" then
|
|
cmd = string.format("setfattr -x user.timemarks %q", path)
|
|
else
|
|
cmd = string.format("setfattr -n user.timemarks -v %q %q", comment, path)
|
|
end
|
|
os.execute(cmd)
|
|
end
|
|
|
|
local function parse_time(tstr)
|
|
local h, m, s
|
|
if tstr:match("^%d+:%d+:%d+$") then
|
|
h, m, s = tstr:match("^(%d+):(%d+):(%d+)$")
|
|
return tonumber(h) * 3600 + tonumber(m) * 60 + tonumber(s)
|
|
elseif tstr:match("^%d+:%d+$") then
|
|
m, s = tstr:match("^(%d+):(%d+)$")
|
|
return tonumber(m) * 60 + tonumber(s)
|
|
end
|
|
end
|
|
|
|
local function format_time(sec)
|
|
local h = math.floor(sec / 3600)
|
|
local m = math.floor((sec % 3600) / 60)
|
|
local s = math.floor(sec % 60)
|
|
if h > 0 then
|
|
return string.format("%d:%02d:%02d", h, m, s)
|
|
else
|
|
return string.format("%02d:%02d", m, s)
|
|
end
|
|
end
|
|
|
|
local function to_timemark(time_str, title)
|
|
local sec = parse_time(time_str)
|
|
if not sec then return end
|
|
return { time = sec, title = (#title > 0) and title or "" }
|
|
end
|
|
|
|
local function get_timemarks()
|
|
local path = mp.get_property("path")
|
|
if not path then return end
|
|
local comment = get_xattr_comment(path)
|
|
if not comment then return end
|
|
|
|
local marks = {}
|
|
for time_str, title in comment:gmatch("{(%d+:%d+:%d+)%s*([^:}]*)}") do
|
|
local mark = to_timemark(time_str, title)
|
|
if mark then table.insert(marks, mark) end
|
|
end
|
|
for time_str, title in comment:gmatch("{(%d+:%d+)%s*([^:}]*)}") do
|
|
local mark = to_timemark(time_str, title)
|
|
if mark then table.insert(marks, mark) end
|
|
end
|
|
table.sort(marks, function(a, b) return a.time < b.time end)
|
|
return marks
|
|
end
|
|
|
|
local function create_chapters()
|
|
local marks = get_timemarks()
|
|
if marks and #marks > 0 then
|
|
mp.set_property_native("chapter-list", marks)
|
|
end
|
|
end
|
|
|
|
local function get_current_and_next_chapter()
|
|
local chapter_list = mp.get_property_native("chapter-list")
|
|
if not chapter_list or #chapter_list == 0 then return end
|
|
local current_time = mp.get_property_number("time-pos", 0)
|
|
|
|
local current_chapter, next_chapter
|
|
for _, chapter in ipairs(chapter_list) do
|
|
if current_time >= chapter.time then
|
|
current_chapter = chapter
|
|
else
|
|
next_chapter = chapter
|
|
break
|
|
end
|
|
end
|
|
return current_chapter, next_chapter
|
|
end
|
|
|
|
local function append_current_time()
|
|
local path = mp.get_property("path")
|
|
if not path then return end
|
|
local pos = mp.get_property_number("time-pos")
|
|
if not pos then return end
|
|
local tstr = format_time(pos)
|
|
local comment = get_xattr_comment(path) or ""
|
|
comment = comment .. "{" .. tstr .. "}"
|
|
set_xattr_comment(path, comment)
|
|
mp.osd_message("添加时间标记:" .. tstr)
|
|
create_chapters()
|
|
end
|
|
|
|
local function remove_current_time()
|
|
local current_chapter = get_current_and_next_chapter()
|
|
if not current_chapter then return end
|
|
|
|
mp.commandv("seek", current_chapter.time, "absolute")
|
|
local tstr = format_time(current_chapter.time)
|
|
local path = mp.get_property("path")
|
|
if not path then return end
|
|
local comment = get_xattr_comment(path) or ""
|
|
comment = comment:gsub("{" .. tstr .. "%s*[^}]*}", "")
|
|
set_xattr_comment(path, comment)
|
|
mp.osd_message("删除时间标记:" .. tstr)
|
|
local marks = get_timemarks()
|
|
if marks and #marks > 0 then
|
|
mp.set_property_native("chapter-list", marks)
|
|
else
|
|
-- 必须清空所有章节信息
|
|
mp.set_property_native("chapter-list", {})
|
|
end
|
|
end
|
|
|
|
local function modify_current_title()
|
|
local current_chapter = get_current_and_next_chapter()
|
|
if not current_chapter then return end
|
|
|
|
local res = utils.subprocess({
|
|
args = { "kdialog", "--inputbox", "编辑标题", current_chapter.title },
|
|
cancellable = false
|
|
})
|
|
if res.error or res.status ~= 0 then return end
|
|
local input = res.stdout:gsub("^%s+", ""):gsub("%s+$", "")
|
|
|
|
local path = mp.get_property("path")
|
|
if not path then return end
|
|
local comment = get_xattr_comment(path)
|
|
if not comment then return end
|
|
local tstr = format_time(current_chapter.time)
|
|
|
|
if input ~= "" then
|
|
local new_fmt = "{" .. tstr .. " " .. input .. "}"
|
|
comment = comment:gsub("{" .. tstr .. "%s*[^}]*}", new_fmt)
|
|
mp.osd_message("时间标记的新标题:" .. input)
|
|
else
|
|
local new_fmt = "{" .. tstr .. "}"
|
|
comment = comment:gsub("{" .. tstr .. "%s*[^}]*}", new_fmt)
|
|
mp.osd_message("清除时间标记的标题")
|
|
end
|
|
set_xattr_comment(path, comment)
|
|
create_chapters()
|
|
end
|
|
|
|
local mark_combo = {
|
|
enabled = false,
|
|
timer = nil,
|
|
}
|
|
mp.register_script_message('set', function(prop, _)
|
|
if prop ~= "timemark-combo" then return end
|
|
mark_combo.toggle()
|
|
end)
|
|
|
|
function mark_combo.toggle()
|
|
mark_combo.enabled = not mark_combo.enabled
|
|
if mark_combo.enabled then
|
|
local chapter_list = mp.get_property_native("chapter-list")
|
|
if #chapter_list == 0 then
|
|
mark_combo.stop()
|
|
return
|
|
end
|
|
if mp.get_property_native("pause") then
|
|
mp.commandv("set", "pause", "no")
|
|
end
|
|
mp.commandv('script-message-to', 'uosc', 'set', 'timemark-combo', "yes")
|
|
mark_combo.multistep(chapter_list)
|
|
else
|
|
mark_combo.stop()
|
|
end
|
|
end
|
|
|
|
function mark_combo.stop()
|
|
mp.commandv('script-message-to', 'uosc', 'set', 'timemark-combo', "no")
|
|
mark_combo.enabled = false
|
|
if mark_combo.timer then
|
|
mark_combo.timer:kill()
|
|
mark_combo.timer = nil
|
|
end
|
|
end
|
|
|
|
function mark_combo.multistep(chapter_list)
|
|
local min_duration = 10
|
|
if mp.get_property_native("pause") then
|
|
mark_combo.stop()
|
|
return
|
|
end
|
|
|
|
local current_time = mp.get_property_number("time-pos", 0)
|
|
local duration = min_duration + math.random(min_duration / 2)
|
|
local current_chapter, next_chapter = get_current_and_next_chapter()
|
|
|
|
if current_chapter then
|
|
local current_offset = current_time - current_chapter.time
|
|
-- 防止相邻的两个时间标记过近导致错过当前标记,需要先播放完当前标记
|
|
if current_offset < duration then
|
|
-- 当前标记的剩余时长,不可以跳到下个标记
|
|
duration = duration - current_offset
|
|
else
|
|
if next_chapter then
|
|
mp.commandv("seek", next_chapter.time, "absolute")
|
|
else
|
|
-- 从头播放
|
|
next_chapter = chapter_list[1]
|
|
mp.commandv("seek", next_chapter.time, "absolute")
|
|
end
|
|
end
|
|
elseif next_chapter then
|
|
mp.commandv("seek", next_chapter.time, "absolute")
|
|
else
|
|
-- #chapter_list > 0
|
|
end
|
|
|
|
mark_combo.timer = mp.add_timeout(duration, function()
|
|
mark_combo.multistep(chapter_list)
|
|
end)
|
|
end
|
|
|
|
mp.register_event("file-loaded", create_chapters)
|
|
mp.register_event("file-loaded", mark_combo.stop)
|
|
|
|
mp.register_script_message("xattr-append-timemark", append_current_time)
|
|
mp.register_script_message("xattr-remove-timemark", remove_current_time)
|
|
mp.register_script_message("xattr-modify-timemark-title", modify_current_title)
|
|
mp.register_script_message("timemark-combo-toggle", mark_combo.toggle)
|