This commit is contained in:
2026-08-15 17:50:48 +02:00
parent 22246060e6
commit 4dc6687de7
35 changed files with 4978 additions and 1509 deletions
+246
View File
@@ -0,0 +1,246 @@
local msg = require('mp.msg')
local utils = require("mp.utils")
local 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'
local function build_curl_args(url, extra_headers)
local args = {
'curl', '-L', '-s', '--compressed', '--user-agent', user_agent
}
extra_headers = extra_headers or {}
for _, h in ipairs(extra_headers) do
table.insert(args, '-H')
table.insert(args, h)
end
if options.cookie_file and options.cookie_file ~= '' then
table.insert(args, '-b')
table.insert(args, mp.command_native({'expand-path', options.cookie_file}))
end
if options.proxy and options.proxy ~= '' then
table.insert(args, '-x')
table.insert(args, options.proxy)
end
table.insert(args, url)
return args
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 get_bilibili_id_and_page(path)
local bvid, aid, page = nil, nil, nil
if not bvid then
bvid = path:match("/video/(BV[%w]+)")
or path:match("[?&]bvid=(BV[%w]+)")
end
if not aid then
aid = path:match("/video/av([%d]+)")
end
if not page then
page = tonumber(path:match("[?&]p=(%d+)"))
end
return bvid, aid, page or 1
end
local function get_bilibili_pagelist_args(bvid, aid)
local url
if bvid ~= nil then
url = "https://api.bilibili.com/x/player/pagelist?bvid=" .. bvid
else
url = "https://api.bilibili.com/x/player/pagelist?aid=" .. aid
end
local headers = {"Referer: https://www.bilibili.com/video/" .. (bvid or ("av" .. aid))}
return build_curl_args(url, headers)
end
local function resolve_bilibili_cid(path, callback)
-- 扩展支持:普通视频(BV/av)、番剧(ep)、课程(cheese)
local api_bangumi_season = "https://api.bilibili.com/pgc/view/web/season"
local api_cheese_season = "https://api.bilibili.com/pugv/view/web/season"
local q = path
-- 解析普通投稿视频
local bvid, aid, p = get_bilibili_id_and_page(q)
if bvid or aid then
local args = get_bilibili_pagelist_args(bvid, aid)
call_cmd_async(args, function(error, json)
if error then
msg.warn("Failed to request bilibili pagelist: " .. tostring(error))
callback(nil)
return
end
local data = utils.parse_json(json)
local pages = data and data["data"]
if type(pages) ~= "table" then
callback(nil)
return
end
local page_info = pages[p] or pages[1]
local cid = page_info and page_info["cid"]
local part = page_info and page_info["part"]
callback(cid and tostring(cid) or nil, part and tostring(part) or nil)
end)
return
end
-- 番剧、番外等(含 ep
if q:find("bangumi/") and q:find("ep") then
local epid = q:match("ep(%d+)") or q:match("ep(%d+)$")
if not epid then
callback(nil)
return
end
local url = api_bangumi_season .. "?ep_id=" .. epid
local arg = build_curl_args(url)
call_cmd_async(arg, function(error, json)
if error then
msg.warn("Failed to request bilibili bangumi info: " .. tostring(error))
callback(nil)
return
end
local data = utils.parse_json(json)
if not data or data.code ~= 0 or not data.result then
msg.warn("bilibili bangumi api returned error")
callback(nil)
return
end
-- 查找正片
local episodes = data.result.episodes or {}
for _, ep in ipairs(episodes) do
if tostring(ep.id) == tostring(epid) then
callback(tostring(ep.cid), tostring(ep.share_copy or ep.title or ""))
return
end
end
-- 查找 section(花絮等)
if type(data.result.section) == "table" then
for _, sec in ipairs(data.result.section) do
if sec.episodes then
for _, ep in ipairs(sec.episodes) do
if tostring(ep.id) == tostring(epid) then
callback(tostring(ep.cid), tostring(ep.share_copy or ep.title or ""))
return
end
end
end
end
end
callback(nil)
end)
return
end
-- cheese 课程
if q:find("cheese/") and q:find("ep") then
local epid = q:match("ep(%d+)") or q:match("ep(%d+)$")
if not epid then
callback(nil)
return
end
local url = api_cheese_season .. "?ep_id=" .. epid
local arg = build_curl_args(url)
call_cmd_async(arg, function(error, json)
if error then
msg.warn("Failed to request bilibili cheese info: " .. tostring(error))
callback(nil)
return
end
local data = utils.parse_json(json)
if not data or data.code ~= 0 or not data.data then
msg.warn("bilibili cheese api returned error")
callback(nil)
return
end
local episodes = data.data.episodes or {}
for _, ep in ipairs(episodes) do
if tostring(ep.id) == tostring(epid) then
callback(tostring(ep.cid), tostring(ep.title or ""))
return
end
end
callback(nil)
end)
return
end
-- 其它情况返回 nil
callback(nil)
end
local function download_bilibili_danmaku(path, cid, from_menu, callback)
local url = "https://comment.bilibili.com/" .. cid .. ".xml"
local args = build_curl_args(url)
call_cmd_async(args, function(error, out)
if error then
show_message("HTTP request failed, see console for details", 5)
msg.error(error)
callback(false)
return
end
if not out or out == '' then
callback(false)
return
end
save_danmaku_xml(path, out)
load_danmaku(from_menu == nil and true or from_menu)
callback(true)
end)
end
-- 为 bilibli 网站的视频播放加载弹幕
function load_danmaku_for_bilibili(path, callback)
callback = callback or function() end
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 then
resolve_bilibili_cid(path, function(resolved_cid)
if resolved_cid then
download_bilibili_danmaku(path, resolved_cid, true, callback)
else
show_message("获取哔哩哔哩视频cid失败", 3)
msg.error("获取哔哩哔哩视频cid失败")
callback(false)
end
end)
return
end
if cid ~= nil then
download_bilibili_danmaku(path, cid, true, callback)
end
end