local utils = require 'mp.utils' local msg = require 'mp.msg' local Source = { ["b 站"] = "bilibili1", ["腾讯"] = "qq", ["爱奇艺"] = "qiyi", ["优酷"] = "youku", } local function load_extra_danmaku(url, episode, number, class, id, site, title, year) local play_url = nil if url:match("^.-%.html") then play_url = url:match("^(.-%.html).*") elseif url:match("^https?://v%.youku%.com/") and url:match("[?&]vid=") then -- 转换 youku 的短链接形式 video?vid=... 到真实播放页 v_show/id_*.html local vid = url:match("[?&]vid=([^&]+)") if vid then play_url = "https://v.youku.com/v_show/id_" .. vid .. ".html" else play_url = url:gsub("%?bsource=360ogvys$",""):gsub("&.*$","") end else play_url = url:gsub("%?bsource=360ogvys$",""):gsub("&.*$","") end ENABLED = true DANMAKU.anime = title .. " (" .. year .. ")" DANMAKU.episode = "第" .. episode .. "话" DANMAKU.source = site DANMAKU.extra = { id = id, site = site, year = year, class = class, title = title, number = tonumber(number), episodenum = tonumber(episode), } write_history() add_danmaku_source(play_url, true) end local function query_tmdb(title, class, menu) local encoded_title = url_encode(title) local url = string.format("https://api.tmdb.org/3/search/%s?api_key=%s&query=%s&language=zh-CN", class, Base64.decode(options.tmdb_api_key), encoded_title) local cmd = { "curl", "-s", "-H", "accept: application/json", url } if options.proxy ~= "" then table.insert(cmd, '-x') table.insert(cmd, options.proxy) end local res = mp.command_native({ name = "subprocess", args = cmd, capture_stdout = true, capture_stderr = true, }) local data = utils.parse_json(res.stdout) if not res.status or res.status ~= 0 or not data.results or #data.results == 0 then local message = "获取 tmdb 中文数据失败" if uosc_available then update_menu_uosc(menu.type, menu.title, message, menu.footnote, menu.cmd, title) else show_message(message, 3) end msg.error("获取 tmdb 中文数据失败:" .. res.stdout) else if class == "tv" then return data.results[1].name else return data.results[1].title end end end local function get_number(cat, id, site) local url = string.format("https://api.web.360kan.com/v1/detail?cat=%s&id=%s&site=%s", cat, id, site) local cmd = { "curl", "-s", url } local res = mp.command_native({ name = "subprocess", args = cmd, capture_stdout = true, capture_stderr = true, }) if not res.status or res.status ~= 0 then msg.error("Failed to fetch data: " .. (res.stderr or "unknown error")) return nil end local result = utils.parse_json(res.stdout) if result and result.data and result.data.allupinfo then return tonumber(result.data.allupinfo[site]) end return nil end local function get_episodes_v2(cat, id, site) local s_param = string.format('[{"cat_id":"%s","ent_id":"%s","site":"%s"}]', tostring(cat), tostring(id), tostring(site)) local url = string.format("https://api.so.360kan.com/episodesv2?v_ap=1&s=%s", url_encode(s_param)) local cmd = { "curl", "-s", url } local res = mp.command_native({ name = "subprocess", args = cmd, capture_stdout = true, capture_stderr = true, }) if not res.status or res.status ~= 0 then msg.error("Failed to fetch episodesv2: " .. (res.stderr or "unknown error")) return nil end local data_text = res.stdout or "" -- 兼容 JSONP 和 纯 JSON:提取最外层括号内 JSON local json_payload = data_text local first_paren = data_text:find('%(') local last_paren = data_text:match('.*()%)') if first_paren and last_paren and last_paren > first_paren then json_payload = data_text:sub(first_paren + 1, last_paren - 1) end local parsed = utils.parse_json(json_payload) if not parsed then msg.error("episodesv2: 解析返回失败: " .. (res.stdout or "")) return nil end local episodes = {} if parsed.code == 0 and parsed.data and #parsed.data > 0 then local seriesHTML = parsed.data[1] and parsed.data[1].seriesHTML if seriesHTML and seriesHTML.seriesPlaylinks then for i, ep in ipairs(seriesHTML.seriesPlaylinks) do local episode_url = nil if type(ep) == 'string' then episode_url = ep elseif type(ep) == 'table' and ep.url then episode_url = ep.url end if episode_url and episode_url ~= '' then table.insert(episodes, { index = i, url = episode_url }) end end end end if #episodes == 0 then return nil end return episodes end function get_details(class, id, site, title, year, number, episodenum) local message = episodenum and "查询弹幕中..." or "加载数据中..." local menu_type = "menu_details" local menu_title = "剧集信息" local footnote = "使用 / 打开筛选" if uosc_available and not episodenum then update_menu_uosc(menu_type, menu_title, message, footnote) else show_message(message, 3) end local cat = 0 if class == "电影" then cat = 1 elseif class == "电视剧" then cat = 2 -- elseif class == "综艺" then -- cat = 3 elseif class == "动漫" then cat = 4 end local items = {} local episodes = nil if cat == 2 or cat == 4 then episodes = get_episodes_v2(cat, id, site) end -- 统一构建 episode_rows:优先使用 episodesv2 返回的数据,否则使用 v1/detail local episode_rows = nil if episodes then episode_rows = {} for _, ep in ipairs(episodes) do table.insert(episode_rows, { index = tostring(ep.index), url = ep.url }) end else if not number and cat ~= 0 then number = get_number(cat, id, site) end if not number or cat == 0 then local message = "无结果" if uosc_available and not episodenum then update_menu_uosc(menu_type, menu_title, message, footnote) else show_message(message, 3) end msg.verbose("无结果") return end local url = string.format("https://api.web.360kan.com/v1/detail?cat=%s&id=%s&start=1&end=%s&site=%s", cat, id, number, site) local cmd = { "curl", "-s", url } local res = mp.command_native({ name = "subprocess", args = cmd, capture_stdout = true, capture_stderr = true, }) if not res.status or res.status ~= 0 then local message = "无结果" if uosc_available and not episodenum then update_menu_uosc(menu_type, menu_title, message, footnote) else show_message(message, 3) end msg.verbose("无结果") return end local result = utils.parse_json(res.stdout) if result and result.data and result.data.allepidetail and result.data.allepidetail[site] then episode_rows = {} for _, it in ipairs(result.data.allepidetail[site]) do table.insert(episode_rows, { index = tostring(it.playlink_num), url = it.url }) end end end if episode_rows and #episode_rows > 0 then if episodenum then for _, ep in ipairs(episode_rows) do if tonumber(ep.index) == tonumber(episodenum) then load_extra_danmaku(ep.url, ep.index, number, class, id, site, title, year) return end end end table.insert(items, { title = "← 返回搜索结果", value = { "script-message-to", "uosc", "open-menu", latest_menu_anime }, keep_open = false, selectable = true, }) for _, ep in ipairs(episode_rows) do table.insert(items, { title = "第" .. ep.index .. "集", hint = ep.index, value = { "script-message-to", mp.get_script_name(), "add-extra-event", ep.url, ep.index, tostring(number), class, id, site, title, year }, }) end end if #items > 0 then if uosc_available and not episodenum then update_menu_uosc(menu_type, menu_title, items, footnote) elseif not episodenum then show_message("", 0) mp.add_timeout(0.1, function() open_menu_select(items) end) end else local message = "无结果" if uosc_available and not episodenum then update_menu_uosc(menu_type, menu_title, message, footnote) else show_message(message, 3) end msg.verbose("无结果") end end local function search_query(query, class, menu) local url = string.format("https://api.so.360kan.com/index?force_v=1&kw=%s", query) if class ~= nil then url = url .. "&type=" .. class end local cmd = { "curl", "-s", url } local res = mp.command_native({ name = "subprocess", args = cmd, capture_stdout = true, capture_stderr = true, }) if not res.status or res.status ~= 0 then local message = "无结果" if uosc_available then update_menu_uosc(menu.type, menu.title, message, menu.footnote, menu.cmd, query) else show_message(message, 3) end msg.verbose("无结果") return end local result = utils.parse_json(res.stdout) local items = {} if result and result.data.longData and result.data.longData.rows then for _, item in ipairs(result.data.longData.rows) do if item.playlinks then for source_name, source_id in pairs(Source) do if item.playlinks[source_id] then table.insert(items, { title = item.titleTxt, hint = item.cat_name .. " | " .. item.year .. " | 来源:" .. source_name, value = { "script-message-to", mp.get_script_name(), "get-extra-event", item.cat_name, item.en_id, item.playlinks[source_id], source_id, item.titleTxt, item.year, }, }) end end end end end if #items > 0 then if uosc_available then latest_menu_anime = update_menu_uosc(menu.type, menu.title, items, menu.footnote, menu.cmd, query) else show_message("", 0) mp.add_timeout(0.1, function() open_menu_select(items) end) end else local message = "无结果" if uosc_available then update_menu_uosc(menu.type, menu.title, message, menu.footnote, menu.cmd, query) else show_message(message, 3) end msg.verbose("无结果") end end function query_extra(name, class) local name = name:gsub("%s*%(%d-%)%s*$", "") local title = nil local class = class and class:lower() local message = "加载数据中..." local menu = { type = "menu_anime", title = "在此处输入番剧名称", footnote = "使用enter或ctrl+enter进行搜索" } menu.cmd = { "script-message-to", mp.get_script_name(), "search-anime-event" } if uosc_available then update_menu_uosc(menu.type, menu.title, message, menu.footnote, menu.cmd, name) else show_message(message, 30) end if is_chinese(name) then search_query(name, class, menu) return end if options.tmdb_api_key == "" or #Base64.decode(options.tmdb_api_key) < 32 then local message = "请正确设置 tmdb_api_key 或尝试使用中文搜索" if uosc_available then update_menu_uosc(menu.type, menu.title, message, menu.footnote, menu.cmd, name) else show_message(message, 3) end return end if class == "dy" then title = query_tmdb(name, "movie", menu) else title = query_tmdb(name, "tv", menu) end if title then search_query(title, class, menu) end end mp.register_script_message("get-extra-event", function(cat, id, playlink, source_id, title, year) if uosc_available then mp.commandv("script-message-to", "uosc", "close-menu", "menu_anime") end if cat == "电影" then if playlink:match("^.-%.html") then playlink = playlink:match("^(.-%.html).*") else playlink = playlink:gsub("%?bsource=360ogvys$","") end DANMAKU.anime = title .. " (" .. year .. ")" DANMAKU.episode = "电影" DANMAKU.source = source_id write_history() add_danmaku_source(playlink, true) else get_details(cat, id, source_id, title, year) end end) mp.register_script_message("add-extra-event", function(url, episode, number, class, id, site, title, year) if uosc_available then mp.commandv("script-message-to", "uosc", "close-menu", "menu_details") end load_extra_danmaku(url, episode, number, class, id, site, title, year) end)