上传文件至「js」

This commit is contained in:
2026-08-14 11:21:48 +02:00
parent 097ff601b9
commit 1df1c70e5e
5 changed files with 1723 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
//本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
//严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
import {
load as t
} from "assets://js/lib/cat.js";
let e, a = "https://www.985djw.com",
i = "",
n = {
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36",
Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"accept-language": "zh-CN,zh;q=0.9",
"cache-control": "no-cache",
pragma: "no-cache",
priority: "u=0, i",
"sec-ch-ua": '"Google Chrome";v="149", "Chromium";v="149", "Not)A;Brand";v="24"',
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": '"Windows"',
"sec-fetch-dest": "document",
"sec-fetch-mode": "navigate",
"upgrade-insecure-requests": "1"
};
async function r(t) {
"function" == typeof js2Proxy && "function" == typeof desX && "function" != typeof getProxy && (e = !0);
let n = t.ext;
if ("object" == typeof n && null !== n) {
let t = n.host || "";
t && t.startsWith("http") && (a = t), n.picProxy && n.picProxy.startsWith("http") && (i = n.picProxy)
} else "string" == typeof n && n.startsWith("http") && (a = n);
a.endsWith("/") && (a = a.slice(0, -1))
}
async function s(i) {
let n = [],
r = {},
s = await u(a + "/"),
o = t(s);
if (o(".stui-header__menu ul li a").each((t, e) => {
let a = o(e).attr("href"),
i = o(e).text().trim();
if (a && "/" !== a && -1 === a.indexOf(".html")) {
let t = a.match(/\/nl\/([^\/]+)\//);
t && n.push({
type_id: t[1],
type_name: i
})
}
}), e && n.unshift({
type_id: "home",
type_name: "首页"
}), n.length > 0) {
let i = e && n.length > 1 ? n[1].type_id : n[0].type_id,
s = await u(a + "/nl/" + i + "/", a + "/"),
o = t(s),
l = {
"类型": "class",
"地区": "area",
"年份": "year",
"语言": "lang",
"排序": "by"
},
c = {
class: 3,
area: 1,
year: 11,
lang: 4,
by: 2
},
p = [];
o(".stui-screen__list").each((t, e) => {
let a = o(e).find("li:first-child span.text-muted").text().replace("按", "").trim(),
i = l[a];
if (i) {
let t = [],
n = "";
o(e).find("a").each((e, a) => {
let r = o(a).text().trim(),
s = (o(a).attr("href") || "").split("-")[c[i]] || "";
"" === r && "" === s || ("全部" === r && (n = s), t.push({
n: r,
v: s
}))
}), t.length > 1 && p.push({
key: i,
name: a,
init: n,
value: t
})
}
});
for (let t = 0; t < n.length; t++) "home" !== n[t].type_id && (r[n[t].type_id] = p)
}
return JSON.stringify({
class: n,
filters: r
})
}
async function o() {
return await l("home", 1, null, {})
}
async function l(e, i, n, r) {
let s = i || 1;
if ("home" === e) {
let e = await u(a + "/"),
i = h(t(e));
return JSON.stringify({
page: 1,
pagecount: 1,
list: i
})
}
let o = r || {},
l = o.area || "",
c = o.by || "",
p = o.class || "",
d = o.lang || "",
m = o.letter || "",
f = o.year || "",
_ = a + `/vodshow/${e}-${l}-${c}-${p}-${d}-${m}---${s}---${f}/`,
y = await u(_, a + "/nl/" + e + "/"),
g = t(y),
v = h(g),
x = s,
w = g(".stui-page .num").text();
return w && w.includes("/") && (x = parseInt(w.split("/")[1])), JSON.stringify({
page: parseInt(s),
pagecount: parseInt(x),
list: v
})
}
async function c(e, i, n) {
let r = n || 1,
s = "";
s = "1" === String(r) ? a + "/vodsearch/-------------/?wd=" + encodeURIComponent(e) + "&submit=" : a + `/vodsearch/${encodeURIComponent(e)}----------${r}---/`;
let o = await u(s, a + "/"),
l = t(o),
c = h(l),
p = r,
d = l(".stui-page .num").text();
return d && d.includes("/") && (p = parseInt(d.split("/")[1])), JSON.stringify({
page: parseInt(r),
pagecount: parseInt(p),
list: c
})
}
async function p(e) {
let i = e.startsWith("http") ? e : a + e,
n = await u(i, a + "/"),
r = t(n),
s = {
vod_id: e,
vod_name: r("h1.title a").text().trim(),
vod_pic: m(r(".stui-content__thumb img").attr("data-original")),
type_name: "",
vod_year: "",
vod_area: "",
vod_remarks: "",
vod_actor: "",
vod_director: "",
vod_content: r(".stui-content__desc").text().trim(),
vod_play_from: "",
vod_play_url: ""
};
r(".stui-content__detail p.data").each((t, e) => {
let a = r(e).text();
a.includes("类型:") && (s.type_name = r(e).find("a").text().trim()), a.includes("地区:") && (s.vod_area = r(e).find("a").text().trim()), a.includes("年份:") && (s.vod_year = r(e).find("a").text().trim()), a.includes("主演:") && (s.vod_actor = a.replace("主演:", "").trim()), a.includes("导演:") && (s.vod_director = a.replace("导演:", "").trim())
});
let o = [];
return r(".stui-pannel").each((t, e) => {
r(e).find(".stui-pannel__head h3.title").text().includes("手机在线播放器") && (s.vod_play_from = "碎光在线", r(e).find("ul.stui-content__playlist li a").each((t, e) => {
let a = r(e).attr("href"),
i = r(e).text().trim();
a && o.push(i + "$" + a)
}))
}), s.vod_play_url = o.join("#"), JSON.stringify({
list: [s]
})
}
async function d(t, e, i) {
let r = e.startsWith("http") ? e : a + e,
s = "",
o = (await u(r, a + "/")).match(/player_aaaa\s*=\s*(\{[\s\S]*?\})/);
if (o) try {
s = JSON.parse(o[1]).url || ""
} catch (t) {
console.error("解析 player_aaaa 核心数组失败: " + t.message)
}
return JSON.stringify({
parse: 0,
url: s,
header: {
"User-Agent": n["User-Agent"]
}
})
}
async function u(t, e) {
let a = Object.assign({}, n);
return e ? (a.referer = e, a["sec-fetch-site"] = "same-origin") : a["sec-fetch-site"] = "none", (await req(t, {
headers: a
})).content
}
function h(t) {
let e = [];
return t(".stui-vodlist__item").each((a, i) => {
let n = t(i).find("a.stui-vodlist__thumb"),
r = n.attr("href");
r && e.push({
vod_id: r,
vod_name: n.attr("title") || "",
vod_pic: m(n.attr("data-original")),
vod_remarks: n.find(".pic-text").text().trim() || ""
})
}), e
}
function m(t) {
return t ? (t.startsWith("//") ? t = "http:" + t : t.startsWith("/") && (t = a + t), i ? i + t : t) : ""
}
export function __jsEvalReturn() {
return {
init: r,
home: s,
homeVod: o,
category: l,
search: c,
detail: p,
play: d
}
}
+446
View File
@@ -0,0 +1,446 @@
import { load, Crypto } from 'assets://js/lib/cat.js';
var HOST;
const PC_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36';
const DefHeader = { 'User-Agent': PC_UA };
const KParams = {
headers: { 'User-Agent': PC_UA },
timeout: 5000
};
async function init(cfg) {
try {
let host = cfg.ext?.host?.trim() || 'https://www.agedm.io';
HOST = host.replace(/\/$/, '');
KParams.headers.Referer = HOST;
const parseTimeout = parseInt(cfg.ext?.timeout?.trim(), 10);
KParams.timeout = parseTimeout > 0 ? parseTimeout : 5000;
KParams.resHtml = await request(HOST);
} catch (e) {
console.error('初始化参数失败:', e.message);
}
}
async function home(filter) {
try {
const classes = [
{ type_id: 'all', type_name: '全部' },
{ type_id: 'TV', type_name: 'TV' },
{ type_id: '剧场版', type_name: '剧场版' },
{ type_id: 'OVA', type_name: 'OVA' }
];
const filterConfig = [
{
key: "area", name: "地区",
value: [
{ n: "全部", v: "all" }, { n: "日本", v: "日本" },
{ n: "中国", v: "中国" }, { n: "欧美", v: "欧美" }
]
},
{
key: "letter", name: "首字母",
value: [
{ n: "全部", v: "all" },
{ n: "A", v: "A" }, { n: "B", v: "B" }, { n: "C", v: "C" }, { n: "D", v: "D" },
{ n: "E", v: "E" }, { n: "F", v: "F" }, { n: "H", v: "H" }, { n: "I", v: "I" },
{ n: "J", v: "J" }, { n: "K", v: "K" }, { n: "L", v: "L" }, { n: "M", v: "M" },
{ n: "N", v: "N" }, { n: "O", v: "O" }, { n: "P", v: "P" }, { n: "Q", v: "Q" },
{ n: "R", v: "R" }, { n: "S", v: "S" }, { n: "T", v: "T" }, { n: "U", v: "U" },
{ n: "V", v: "V" }, { n: "W", v: "W" }, { n: "X", v: "X" }, { n: "Y", v: "Y" },
{ n: "Z", v: "Z" }
]
},
{
key: "year", name: "年份",
value: [
{ n: "全部", v: "all" },
{ n: "2026", v: "2026" }, { n: "2025", v: "2025" }, { n: "2024", v: "2024" },
{ n: "2023", v: "2023" }, { n: "2022", v: "2022" }, { n: "2021", v: "2021" },
{ n: "2020", v: "2020" }, { n: "2019", v: "2019" }, { n: "2018", v: "2018" },
{ n: "2017", v: "2017" }, { n: "2016", v: "2016" }, { n: "2015", v: "2015" },
{ n: "2014", v: "2014" }, { n: "2013", v: "2013" }, { n: "2012", v: "2012" },
{ n: "2011", v: "2011" }, { n: "2010", v: "2010" }, { n: "2009", v: "2009" },
{ n: "2008", v: "2008" }, { n: "2007", v: "2007" }, { n: "2006", v: "2006" },
{ n: "2005", v: "2005" }, { n: "2004", v: "2004" }, { n: "2003", v: "2003" },
{ n: "2002", v: "2002" }, { n: "2001", v: "2001" }, { n: "2000以前", v: "2000以前" }
]
},
{
key: "quarter", name: "季度",
value: [
{ n: "全部", v: "all" },
{ n: "1月", v: "1" }, { n: "4月", v: "4" },
{ n: "7月", v: "7" }, { n: "10月", v: "10" }
]
},
{
key: "status", name: "状态",
value: [
{ n: "全部", v: "all" },
{ n: "连载", v: "连载" }, { n: "完结", v: "完结" }, { n: "未播放", v: "未播放" }
]
},
{
key: "type", name: "类型",
value: [
{ n: "全部", v: "all" },
{ n: "搞笑", v: "搞笑" },{ n: "运动", v: "运动" },{ n: "励志", v: "励志" },
{ n: "热血", v: "热血" },{ n: "战斗", v: "战斗" },{ n: "竞技", v: "竞技" },
{ n: "校园", v: "校园" },{ n: "青春", v: "青春" },{ n: "爱情", v: "爱情" },
{ n: "恋爱", v: "恋爱" },{ n: "冒险", v: "冒险" },{ n: "后宫", v: "后宫" },
{ n: "百合", v: "百合" },{ n: "治愈", v: "治愈" },{ n: "萝莉", v: "萝莉" },
{ n: "魔法", v: "魔法" },{ n: "悬疑", v: "悬疑" },{ n: "推理", v: "推理" },
{ n: "奇幻", v: "奇幻" },{ n: "科幻", v: "科幻" },{ n: "游戏", v: "游戏" },
{ n: "神魔", v: "神魔" },{ n: "恐怖", v: "恐怖" },{ n: "血腥", v: "血腥" },
{ n: "机战", v: "机战" },{ n: "战争", v: "战争" },{ n: "犯罪", v: "犯罪" },
{ n: "历史", v: "历史" },{ n: "社会", v: "社会" },{ n: "职场", v: "职场" },
{ n: "剧情", v: "剧情" },{ n: "伪娘", v: "伪娘" },{ n: "耽美", v: "耽美" },
{ n: "童年", v: "童年" },{ n: "教育", v: "教育" },{ n: "亲子", v: "亲子" },
{ n: "真人", v: "真人" },{ n: "歌舞", v: "歌舞" },{ n: "肉番", v: "肉番" },
{ n: "美少女", v: "美少女" },{ n: "轻小说", v: "轻小说" },{ n: "吸血鬼", v: "吸血鬼" },
{ n: "女性向", v: "女性向" },{ n: "泡面番", v: "泡面番" },{ n: "欢乐向", v: "欢乐向" }
]
},
{
key: "source", name: "资源",
value: [
{ n: "全部", v: "all" },
{ n: "BDRIP", v: "BDRIP" }, { n: "AGE-RIP", v: "AGE-RIP" }
]
},
{
key: "sort", name: "排序",
value: [
{ n: "全部", v: "time" },
{ n: "时间", v: "time" }, { n: "点击量", v: "点击量" }
]
}
];
const filters = { all: filterConfig, TV: filterConfig, "剧场版": filterConfig, OVA: filterConfig };
return JSON.stringify({ class: classes, filters });
} catch (e) {
console.error('获取分类失败:', e.message);
return JSON.stringify({ class: [], filters: {} });
}
}
async function homeVod() {
try {
const VODS = getVodList(KParams.resHtml, true);
return JSON.stringify({ list: VODS });
} catch (e) {
console.error('推荐页获取失败:', e.message);
return JSON.stringify({ list: [] });
}
}
async function category(tid, pg, filter, extend) {
try {
pg = parseInt(pg, 10) || 1;
const year = extend?.year ? encodeURIComponent(extend.year) : 'all';
const letter = extend?.letter ? encodeURIComponent(extend.letter) : 'all';
const type = extend?.type ? encodeURIComponent(extend.type) : 'all';
const source = extend?.source ? encodeURIComponent(extend.source) : 'all';
const sort = extend?.sort ? encodeURIComponent(extend.sort) : 'time';
const area = extend?.area ? encodeURIComponent(extend.area) : 'all';
const quarter = extend?.quarter ? encodeURIComponent(extend.quarter) : 'all';
const status = extend?.status ? encodeURIComponent(extend.status) : 'all';
const cateUrl = `${HOST}/catalog/${tid}-${year}-${letter}-${type}-${source}-${sort}-${pg}-${area}-${quarter}-${status}`;
const resHtml = await request(cateUrl);
const VODS = getVodList(resHtml);
return JSON.stringify({ list: VODS, page: pg, pagecount: 999, limit: 30, total: 30 * 999 });
} catch (e) {
console.error('类别页获取失败:', e.message);
return JSON.stringify({ list: [], page: 1, pagecount: 0, limit: 30, total: 0 });
}
}
async function search(wd, quick, pg) {
try {
pg = parseInt(pg, 10) || 1;
const searchUrl = `${HOST}/search?query=${encodeURIComponent(wd)}&page=${pg}`;
const resHtml = await request(searchUrl);
const VODS = getVodList(resHtml);
return JSON.stringify({ list: VODS, page: pg, pagecount: 10, limit: 30, total: 300 });
} catch (e) {
console.error('搜索页获取失败:', e.message);
return JSON.stringify({ list: [], page: 1, pagecount: 0, limit: 30, total: 0 });
}
}
function getVodList(khtml, hm = false) {
try {
if (!khtml) throw new Error('源码为空');
const kvods = [];
const $ = load(khtml);
const listArr = hm ? $('.video_item').get() : $('.video_cover').get();
for (const it of listArr) {
const $it = $(it);
const a = $it.find('a');
const kname = dealStr(a.text().trim() || a.attr('title'), '名称');
const kurl = dealStr(a.attr('href'), 'href');
const poster = $it.find('img');
let pic = poster.attr('data-original') || poster.attr('src');
if (pic && pic.startsWith('/')) pic = HOST + pic;
const kpic = dealStr(pic, '图片');
const kremarks = dealStr($it.find('span').text(), '状态');
kvods.push({
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
vod_id: `${kurl}@${kname}@${kpic}@${kremarks}`
});
}
return kvods;
} catch (e) {
console.error(`生成视频列表失败:`, e.message);
return [];
}
}
async function detail(ids) {
try {
const [id, kname, kpic, kremarks] = ids.split('@');
const rawUrl = /^http/.test(id) ? id : `${HOST}${id}`;
const detailUrl = rawUrl.replace(/https?:\/\/[^\/]+/, HOST);
const resHtml = await request(detailUrl);
if (!resHtml) throw new Error('源码请求失败');
const $ = load(resHtml);
const intros = $('.detail_imform_list').html() || '';
const ktabs = $('.nav-item:has(button)').get().map((it, idx) => {
const btn = $(it).find('button');
return dealStr(`${btn.text()}(${btn.attr('data-bs-target').replace('#playlist-source-','')})`, `线-${idx+1}`);
});
const kurls = $('.video_detail_episode').get().map(item => {
const arr = $(item).find('li a').get().map(it => {
const $it = $(it);
return dealStr($it.text(), 'noEpi') + '$' + dealStr($it.attr('href'), 'noUrl');
});
return arr.join('#');
});
const VOD = {
vod_id: detailUrl,
vod_name: kname,
vod_pic: kpic,
type_name: cutStr(intros, '剧情类型:', '</li', '类型'),
vod_remarks: kremarks,
vod_year: cutStr(intros, '首播时间:', '</li', '1000'),
vod_area: cutStr(intros, '地区:', '</li', '地区'),
vod_lang: '语言',
vod_director: cutStr(intros, '原作:', '</li', '原作'),
vod_actor: cutStr(intros, '主演:', '</li', '主演'),
vod_content: cutStr(intros, 'video_detail_desc£>', '</div>', '简介'),
vod_play_from: ktabs.join('$$$'),
vod_play_url: kurls.join('$$$')
};
return JSON.stringify({ list: [VOD] });
} catch (e) {
console.error('详情页获取失败:', e.message);
return JSON.stringify({ list: [] });
}
}
function uuid() {
const chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
const arr = [];
arr[8] = arr[13] = arr[18] = arr[23] = "-";
arr[14] = "4";
for (let i = 0; i < 36; i++) {
if (!arr[i]) {
const r = Math.random() * 16 | 0;
arr[i] = chars[(i === 19 ? (r & 0x3 | 0x8) : r)];
}
}
return arr.join("");
}
async function play(flag, ids, flags) {
try {
const playUrl = /^http/.test(ids) ? ids : `${HOST}${ids}`;
let kurl = '';
const resHtml = await request(playUrl);
const $ = load(resHtml);
const iframesrc = $('#iframeForVideo').attr('src') || '';
if (iframesrc) {
const iframeresHtml = await request(iframesrc);
let rawVurl = cutStr(iframeresHtml, 'Vurl = ', ';');
let Vurl = rawVurl.replace(/^["']|["']$/g, '').trim();
if (!/m3u8|mp4|mkv/.test(Vurl)) {
const Ref = cutStr(iframeresHtml, 'Ref = "', '";');
const Time = cutStr(iframeresHtml, 'Time = "', '";');
const Version = cutStr(iframeresHtml, 'Version = "', '";');
const idList = cutStr(iframeresHtml, 'id="', '"', 'null', true, 1, true);
const mockHtmlMeta = { contentTypeId: idList[0], viewportId: idList[1] };
const apiUrl = 'https://jx.wuzhoupai.com:8443/vip/Api.php';
const jxhost = 'jx.wuzhoupai.com';
const currentUuid = uuid();
const reqData = { url: Vurl, wap: '0', ios: '0', host: jxhost, referer: Ref, time: Time };
const encryptParams = hxm_encrypt(JSON.stringify(reqData)).toUpperCase();
const signRaw = `${jxhost} | ${currentUuid} | ${Time} | ${Version} | ${encryptParams}`;
const sign = hxm_encrypt(signRaw);
const apiRes = await request(apiUrl, {
method: 'post',
postType: 'json',
headers: {
'x-requested-with': 'XMLHttpRequest',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'Accept': 'application/json, text/javascript, */*; q=0.01',
'Video-Parse-Uuid': currentUuid,
'Video-Parse-Time': Time,
'Video-Parse-Version': Version,
'Video-Parse-Sign': sign
},
data: { Params: encryptParams }
});
const apiResjson = safeParseJSON(apiRes);
const result = decryptData(apiResjson, mockHtmlMeta);
kurl = result.url;
} else {
kurl = Vurl;
}
}
return JSON.stringify({ jx: 0, parse: 0, url: kurl, header: DefHeader });
} catch (e) {
console.error('播放失败:', e.message);
return JSON.stringify({ jx: 0, parse: 0, url: '', header: {} });
}
}
// 工具函数
function dealStr(str, defaultValue = '') {
if (str === null || typeof str === 'undefined' || String(str).trim() === '') return defaultValue;
return String(str).replace(/(&nbsp;|\u00A0|\s)+/g, ' ').trim().replace(/\s+/g, ' ') || defaultValue;
}
function cutStr(str, prefix = '', suffix = '', defaultVal = 'cutFaile', clean = true, i = 1, all = false) {
try {
if (typeof str !== 'string' || !str) throw new Error('被截取对象需为非空字符串');
const cleanStr = cs => String(cs).replace(/<[^>]*?>/g, ' ').replace(/(&nbsp;|\u00A0|\s)+/g, ' ').trim().replace(/\s+/g, ' ');
const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&');
const pre = esc(prefix).replace(/£/g, '[^]*?');
const end = esc(suffix);
const regex = new RegExp(`${pre ? pre : '^'}([^]*?)${end ? end : '$'}`, 'g');
const matchIterator = str.matchAll(regex);
if (all) {
const matchArr = [...matchIterator];
return matchArr.length ? matchArr.map(it => {
const val = it[1] ?? defaultVal;
return clean && val !== defaultVal ? cleanStr(val) : val;
}) : [defaultVal];
}
i = parseInt(i, 10);
if (isNaN(i) || i < 1) throw new Error('序号必须为正整数');
let tgIdx = i - 1, matchIdx = 0;
for (const match of matchIterator) {
if (matchIdx++ === tgIdx) {
const result = match[1] ?? defaultVal;
return clean && result !== defaultVal ? cleanStr(result) : result;
}
}
return defaultVal;
} catch (e) {
console.error(`字符串截取失败:`, e.message);
return all ? ['cutErr'] : 'cutErr';
}
}
function safeParseJSON(jStr) {
try { return JSON.parse(jStr); } catch (e) { return null; }
}
async function request(reqUrl, options = {}) {
try {
if (typeof reqUrl !== 'string' || !reqUrl.trim()) throw new Error('reqUrl需为字符串且非空');
if (typeof options !== 'object' || Array.isArray(options) || !options) throw new Error('options类型需为非null对象');
options.method = options.method?.toLowerCase() || 'get';
if (['get', 'head'].includes(options.method)) {
delete options.data;
delete options.postType;
} else {
options.data = options.data ?? '';
options.postType = options.postType?.toLowerCase() || 'form';
}
let { headers, timeout, toBase64 = false, ...restOpts } = options;
const optObj = {
headers: (headers && typeof headers === 'object' && !Array.isArray(headers)) ? headers : KParams.headers,
timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout,
buffer: toBase64 ? 2 : 0,
...restOpts
};
const res = await req(reqUrl, optObj);
if (options.withHeaders) {
const resHeaders = res.headers && typeof res.headers === 'object' && !Array.isArray(res.headers) ? res.headers : {};
return JSON.stringify({ ...resHeaders, body: res?.content ?? '' });
}
return res?.content ?? '';
} catch (e) {
console.error(`${reqUrl}→请求失败:`, e.message);
return options?.withHeaders ? JSON.stringify({ body: '' }) : '';
}
}
// AES解密工具
function decryptOuterAesCbc(hexCipherText, md5String) {
const hexWords = Crypto.enc.Hex.parse(hexCipherText);
const base64CipherText = Crypto.enc.Base64.stringify(hexWords);
const rawKey = md5String.substring(0, 16);
const rawIv = md5String.substring(16, 32);
const cryptoKey = Crypto.enc.Latin1.parse(rawKey);
const cryptoIv = Crypto.enc.Latin1.parse(rawIv);
const decryptedWords = Crypto.AES.decrypt(base64CipherText, cryptoKey, { iv: cryptoIv, mode: Crypto.mode.CBC, padding: Crypto.pad.Pkcs7 });
return Crypto.enc.Utf8.stringify(decryptedWords);
}
function hxm_decrypt(encryptedUrl) {
if (!encryptedUrl) return "";
const secretStr = "ni po jie ni ** ";
const cryptoKey = Crypto.enc.Utf8.parse(secretStr);
const cryptoIv = Crypto.enc.Utf8.parse(secretStr);
let finalCipherText = encryptedUrl;
if (/^[0-9a-fA-F]+$/.test(encryptedUrl)) {
const hexWords = Crypto.enc.Hex.parse(encryptedUrl);
finalCipherText = Crypto.enc.Base64.stringify(hexWords);
}
const decryptedWords = Crypto.AES.decrypt(finalCipherText, cryptoKey, { iv: cryptoIv, mode: Crypto.mode.CBC, padding: Crypto.pad.Pkcs7 });
return Crypto.enc.Utf8.stringify(decryptedWords);
}
function hxm_encrypt(encryptedUrl) {
if (!encryptedUrl) return "";
const secretStr = "ni po jie ni ** ";
const cryptoKey = Crypto.enc.Utf8.parse(secretStr);
const cryptoIv = Crypto.enc.Utf8.parse(secretStr);
let finalCipherText = encryptedUrl;
if (/^[0-9a-fA-F]+$/.test(encryptedUrl)) {
const hexWords = Crypto.enc.Hex.parse(encryptedUrl);
finalCipherText = Crypto.enc.Base64.stringify(hexWords);
}
const encrypted = Crypto.AES.encrypt(finalCipherText, cryptoKey, { iv: cryptoIv, mode: Crypto.mode.CBC, padding: Crypto.pad.Pkcs7 });
return Crypto.enc.Hex.stringify(encrypted.ciphertext).toUpperCase();
}
function decryptData(responseData, htmlMeta = { contentTypeId: '', viewportId: '' }) {
let decryptedInfo = {};
if (responseData.Status !== 1) throw new Error("Status 不为 1,数据异常");
if (responseData.Code === 10) {
let metaFeature = (htmlMeta.contentTypeId + htmlMeta.viewportId).replace("viewport", "");
const rawSaltKey = responseData.Code + metaFeature + responseData.Appkey + responseData.Version;
const md5String = md5X(rawSaltKey);
const decryptedStr = decryptOuterAesCbc(responseData.Data, md5String);
decryptedInfo = JSON.parse(decryptedStr);
if (!decryptedInfo.url.startsWith('http')) decryptedInfo.url = hxm_decrypt(decryptedInfo.url);
decryptedInfo.url = decodeURIComponent(decryptedInfo.url);
} else {
const rawSaltKey = responseData.Code + responseData.Appkey + responseData.Version;
const md5String = md5X(rawSaltKey);
const decryptedStr = decryptOuterAesCbc(responseData.Data, md5String);
decryptedInfo = JSON.parse(decryptedStr);
if (!decryptedInfo.url.startsWith('http')) decryptedInfo.url = hxm_decrypt(decryptedInfo.url);
decryptedInfo.url = decodeURIComponent(decryptedInfo.url);
}
return decryptedInfo;
}
export function __jsEvalReturn() {
return { init, home, homeVod, category, search, detail, play, proxy: null };
}
+149
View File
@@ -0,0 +1,149 @@
let host = 'https://a123tv.com';
let headers = {
'User-Agent': 'Mozilla/5.0 (Linux; Android 13; M2102J2SC Build/TKQ1.221114.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/144.0.7559.31 Mobile Safari/537.36',
'Referer': host
};
function getList(html) {
let videos = [];
pdfa(html, '.w4-item-wrap').forEach(it => {
const id = pdfh(it, 'a.w4-item&&href') || '',
name = pdfh(it, 'img&&alt') || '',
pic = pdfh(it, 'img&&data-src') || pdfh(it, 'img&&src') || '',
remark = pdfh(it, '.w4-item-cover .s span&&Text') || '';
if (id && name) {
let vod_pic = pic.startsWith('//') ? `https:${pic}` : pic.startsWith('/') ? `${host}${pic}` : pic;
vod_pic = vod_pic || 'data:image/gif;base64,R0lGODdhAQABAPAAAMPDwwAAACwAAAAAAQABAAACAkQBADs=';
videos.push({
vod_id: id.replace(host, '').replace(/^\//, ''),
vod_name: name.trim(),
vod_pic,
vod_remarks: remark.trim()
});
}
});
return videos;
}
async function init(cfg) {}
async function home(filter) {
return JSON.stringify({
class: [{ "type_id": "10", "type_name": "电影" }, { "type_id": "11", "type_name": "连续剧" },
{ "type_id": "12", "type_name": "综艺" }, { "type_id": "13", "type_name": "动漫" },
{ "type_id": "15", "type_name": "福利" }],
filters: {}
});
}
async function homeVod() {
let resp = await req(host, { headers });
return JSON.stringify({ list: getList(resp.content) });
}
async function category(tid, pg, filter, extend) {
let p = parseInt(pg) || 1;
let resp = await req(`${host}/t/${tid}/p${p}.html`, { headers });
return JSON.stringify({ list: getList(resp.content), page: p, pagecount: p + 5, limit: 20 });
}
async function detail(id) {
const playFrom = [], playList = [];
let detailUrl = id.startsWith('http') ? id : `${host}/${id.replace(/^\//, '')}`;
let { content: html = '' } = await req(detailUrl, { headers });
// 基础信息
let vod_name = (pdfh(html, 'li.on h1&&Text') || pdfh(html, 'h1.title&&Text') || '未知片名').trim();
let vod_pic = pdfh(html, '#awp1&&data-poster') || '';
vod_pic = vod_pic.startsWith('//') ? `https:${vod_pic}` : vod_pic.startsWith('/') ? `${host}${vod_pic}` : vod_pic;
let vod_remarks = pdfh(html, '.w4-line-cover .i&&Text')?.split(' / ')[1] || '720p';
// 解析meta中的导演/演员/地区/年代/类型/简介
let vod_area = '', vod_year = '', type_name = '', vod_actor = '', vod_director = '', vod_content = '';
// 提取description
const descMatch = html.match(/<meta name="description" content="([^"]+)"[^>]*>/i);
const descText = descMatch ? descMatch[1] : '';
// 从标题提取类型和年代
const titleMatch = html.match(/《[^》]+》\s*-\s*([^-]+)\s*-\s*(\d+)年/i);
if (titleMatch) {
type_name = titleMatch[1].trim();
vod_year = titleMatch[2].trim();
}
// 解析描述中的地区、导演、演员、剧情
if (descText) {
const areaMatch = descText.match(/地区:([^。]+)。/);
const dirMatch = descText.match(/导演:([^。]+)。/);
const actorMatch = descText.match(/演员:([^。]+)。/);
const contentMatch = descText.match(/剧情:([\s\S]+)/);
vod_area = areaMatch ? areaMatch[1].trim() : '';
vod_director = dirMatch ? dirMatch[1].trim().replace(/、/g, '/') : '';
vod_actor = actorMatch ? actorMatch[1].trim().replace(/、/g, '/') : '';
vod_content = contentMatch ? contentMatch[1].trim() : descText;
}
const ppMatch = html.match(/var\s+pp\s*=\s*({[\s\S]*?});/);
if (ppMatch) {
const { la = [] } = JSON.parse(ppMatch[1]);
const episodeNames = pdfa(html, '.w4-episode-list .w a').map((a, i) =>
(pdfh(a, 'a&&Text') || pdfh(a, 'a&&title') || `${i+1}`).trim()
);
la.forEach((item, index) => {
if (Array.isArray(item) && item.length >= 5) {
const [, , episodeNum, , m3u8] = item;
const realEpNum = parseInt(episodeNum) || episodeNames.length;
const lineEpisodes = [];
for (let i = 0; i < realEpNum; i++) {
let epName = episodeNames[i] || `${i+1}`;
let realM3u8 = m3u8.startsWith('//') ? `https:${m3u8}` : m3u8;
lineEpisodes.push(`${epName}$${realM3u8}`);
}
playFrom.push(`✨推荐✨${index + 1}`);
playList.push(lineEpisodes.join('#'));
}
});
}
return JSON.stringify({
list: [{
vod_id: id, vod_name, vod_pic, vod_remarks, vod_year, vod_area,
type_name, vod_actor, vod_director, vod_content,
vod_play_from: playFrom.join('$$$'), vod_play_url: playList.join('$$$')
}]
});
}
async function search(wd, quick, pg) {
let p = parseInt(pg) || 1;
let resp = await req(`${host}/s/${encodeURIComponent(wd)}/p${p}.html`, { headers });
return JSON.stringify({ list: getList(resp.content), page: p, pagecount: p + 5, limit: 20 });
}
async function play(flag, id, flags) {
let url = id.startsWith('http') ? id : `${host}/${id.replace(/^\//, '')}`;
let { content: html = '' } = await req(url, { headers });
const playerVarMatch = html.match(/var\s+player_aaaa\s*=\s*({[^;]+});/);
if (playerVarMatch) {
let jsonStr = playerVarMatch[1].replace(/(\w+):/g, '"$1":').replace(/\\/g, '');
let { url: playUrl } = JSON.parse(jsonStr);
if (playUrl) return JSON.stringify({ parse: playUrl.includes('.m3u8') ? 0 : 1, url: playUrl, header: headers });
}
let m3u8Match = html.match(/"url":"([^"]+\.m3u8(\?[^"]*)?)"/) || html.match(/'url':'([^']+\.m3u8(\?[^']*)?)'/) ||
html.match(/"(https?:\/\/[^"]+\.m3u8[^"]*)"/) || html.match(/'(https?:\/\/[^']+\.m3u8[^']*)'/) ||
html.match(/https?:\/\/[^\s"]+\.m3u8[^\s"]*/);
if (m3u8Match) {
let playUrl = m3u8Match[1] || m3u8Match[0];
return JSON.stringify({ parse: 0, url: playUrl.replace(/\\/g, ""), header: headers });
}
let iframeMatch = html.match(/<iframe[^>]*src="([^"]+)"[^>]*>/i) || html.match(/<video[^>]*src="([^"]+)"[^>]*>/i);
if (iframeMatch) {
let iframeUrl = iframeMatch[1];
iframeUrl = iframeUrl.startsWith('//') ? `https:${iframeUrl}` : !iframeUrl.startsWith('http') ? `${host}${iframeUrl.startsWith('/') ? '' : '/'}${iframeUrl}` : iframeUrl;
return JSON.stringify({ parse: 1, url: iframeUrl, header: headers });
}
return JSON.stringify({ parse: 1, url: url, header: headers });
}
export default { init, home, homeVod, category, detail, search, play };
+538
View File
@@ -0,0 +1,538 @@
import { load } from 'assets://js/lib/cat.js';
import 'assets://js/lib/crypto-js.js';
var BILI_COOKIE = '';
var BILI_GUEST_COOKIE = ''; // 自动获取的访客 Cookie
var extConfig = {};
const PLACEHOLDER = 'https://i0.hdslb.com/bfs/static/jinkela/video/asserts/nocover.png';
const DEFAULT_TYPE = '音乐#新闻#白噪音#沙雕动漫#虾仁#短剧#电影#歌曲#演唱会#鬼畜#搞笑#脑洞乌托邦#相声#小品#戏曲#音乐#MV#舞曲#舞蹈#纪录片#健身#帕梅拉#武术#太极拳#广场舞#体育#球星#世界杯#UP主#小姐姐#女优#美食#食谱#荒野求生#旅游#风景#游戏#解说#演讲#考公考试#平面设计#软件教学'; // 分类兜底
function decodeHtml(str) {
if (!str) return '';
return str.replace(/&quot;/g, '"')
.replace(/&amp;/g, '&')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&#39;/g, "'")
.replace(/&nbsp;/g, ' ');
}
function fmtDuration(str) {
if (!str) return '';
let parts = str.split(':').map(Number);
let minutes = 0;
if (parts.length === 3) {
minutes = parts[0] * 60 + parts[1] + Math.round(parts[2] / 60);
} else if (parts.length === 2) {
minutes = parts[0] + Math.round(parts[1] / 60);
}
return minutes > 0 ? minutes + '分钟' : '';
}
function formatCount(num) {
if (!num && num !== 0) return '';
let n = Number(num);
if (n >= 10000) {
let w = n / 10000;
return (w % 1 === 0 ? w.toFixed(0) : w.toFixed(1)) + '万';
}
return n.toString();
}
const mixinKeyEncTab = [
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5,
49, 33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55,
40, 61, 26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62,
11, 36, 20, 34, 44, 52
];
let wbiKeyCache = { key: '', hour: -1 };
function getMixinKey(orig) {
let temp = '';
for (let i = 0; i < mixinKeyEncTab.length; i++) {
if (mixinKeyEncTab[i] < orig.length) temp += orig[mixinKeyEncTab[i]];
}
return temp.slice(0, 32);
}
async function updateWbiKey() {
const now = new Date();
const hour = now.getHours();
if (wbiKeyCache.key && wbiKeyCache.hour === hour) return;
// 用当前有效的 Cookie 请求 wbi 密钥
const ck = BILI_COOKIE || BILI_GUEST_COOKIE || 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc';
try {
const resp = await req('https://api.bilibili.com/x/web-interface/nav', {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://www.bilibili.com',
'Origin': 'https://www.bilibili.com',
'Cookie': ck
}
});
const data = JSON.parse(resp.content);
const imgUrl = data.data.wbi_img.img_url;
const subUrl = data.data.wbi_img.sub_url;
const imgKey = imgUrl.split('/').pop().split('.')[0];
const subKey = subUrl.split('/').pop().split('.')[0];
wbiKeyCache.key = getMixinKey(imgKey + subKey);
wbiKeyCache.hour = hour;
} catch (e) {
wbiKeyCache.key = '';
}
}
function encWbi(params, key) {
if (!key) return '';
const sortedKeys = Object.keys(params).sort();
const query = sortedKeys.map(k => {
const v = params[k];
return encodeURIComponent(k) + '=' + encodeURIComponent(v).replace(/[!'()*]/g, '');
}).join('&');
const w_rid = CryptoJS.MD5(query + key).toString();
return query + '&w_rid=' + w_rid;
}
async function fetchGuestCookie() {
try {
const resp = await req('https://space.bilibili.com/2/video', {
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
},
timeout: 8000
});
if (resp && resp.headers) {
let cookies = resp.headers['set-cookie'] || [];
if (typeof cookies === 'string') cookies = [cookies];
const parts = cookies.map(c => c.split(';')[0].trim()).filter(c => c);
if (parts.length > 0) BILI_GUEST_COOKIE = parts.join('; ');
}
} catch (e) {
}
}
function getHeaders(useCookie) {
let ck = BILI_COOKIE || BILI_GUEST_COOKIE || 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc';
return {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36',
'Referer': 'https://www.bilibili.com',
'Origin': 'https://www.bilibili.com',
'Cookie': useCookie ? ck : 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc'
};
}
function searchHeaders() {
let ck = BILI_COOKIE || BILI_GUEST_COOKIE || 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc';
return {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36',
'Referer': 'https://www.bilibili.com',
'Origin': 'https://www.bilibili.com',
'Cookie': ck
};
}
async function init(config) {
let cfg = typeof config === 'string' ? JSON.parse(config) : (config || {});
extConfig = cfg;
let cookieSource = cfg.cookie || ''; // 这里填cookie或外链URL
if (cookieSource) {
if (cookieSource.startsWith('http://') || cookieSource.startsWith('https://')) {
try {
let resp = await req(cookieSource, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 8000 });
if (resp && resp.content) BILI_COOKIE = resp.content.trim();
} catch (e) {}
} else {
BILI_COOKIE = cookieSource;
}
}
if (!BILI_COOKIE) {
await fetchGuestCookie();
}
await updateWbiKey();
return '{}';
}
async function home(filter) {
let typeStr = extConfig.type || ''; // 这里填分类URL
for (let retry = 0; retry < 2; retry++) {
if (typeStr.startsWith('http')) {
try {
let resp = await req(typeStr, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 8000 });
if (resp && resp.content) {
let data = JSON.parse(resp.content);
if (data && data.class) return JSON.stringify(data);
}
} catch (e) {
if (retry === 1) print('网络分类加载失败,使用内置分类');
else await new Promise(resolve => setTimeout(resolve, 1000));
}
} else break;
}
let names = (typeStr.indexOf('#') > -1 ? typeStr : DEFAULT_TYPE).split('#').filter(n => n.trim());
let classes = names.map(n => ({ type_id: n, type_name: n }));
let filters = {};
names.forEach(n => {
filters[n] = [
{
key: 'order',
name: '排序',
value: [
{ n: '综合排序', v: 'totalrank' },
{ n: '最多点击', v: 'click' },
{ n: '最新发布', v: 'pubdate' },
{ n: '最多弹幕', v: 'dm' },
{ n: '最多收藏', v: 'stow' }
]
},
{
key: 'duration',
name: '时长',
value: [
{ n: '全部时长', v: '0' },
{ n: '60分钟以上', v: '4' },
{ n: '30~60分钟', v: '3' },
{ n: '10~30分钟', v: '2' },
{ n: '10分钟以下', v: '1' }
]
}
];
});
return JSON.stringify({ class: classes, filters: filters });
}
async function homeVod() { return JSON.stringify({ list: [] }); }
async function category(tid, pg, filterParams, extendParams) {
if (typeof tid === 'string' && tid.endsWith('_clicklink')) tid = tid.split('_')[0];
let page = parseInt(pg) || 1;
let sel = {};
if (filterParams) Object.assign(sel, filterParams);
if (extendParams) Object.assign(sel, extendParams);
let order = sel.order || 'totalrank';
let duration = sel.duration || '0';
let keyword = tid;
if (sel.tid) keyword += ' ' + sel.tid;
await updateWbiKey();
const params = {
search_type: 'video',
keyword: keyword,
order: order,
duration: duration,
page: page
};
const query = encWbi(params, wbiKeyCache.key);
const url = 'https://api.bilibili.com/x/web-interface/wbi/search/type?' + query;
for (let retry = 0; retry < 3; retry++) {
try {
let resp = await req(url, { headers: searchHeaders(), timeout: 8000 });
if (!resp || !resp.content) continue;
let data = JSON.parse(resp.content);
let items = (data.data && data.data.result) || [];
let list = items.map(v => {
let img = v.pic || '';
if (img.startsWith('//')) img = 'https:' + img;
if (!img) img = PLACEHOLDER;
let durationStr = fmtDuration(v.duration);
let playStr = formatCount(v.play);
let danmuStr = formatCount(v.danmaku);
let remarkParts = [];
if (durationStr) remarkParts.push(durationStr);
if (playStr) remarkParts.push(playStr + '播放');
if (danmuStr) remarkParts.push(danmuStr + '弹幕');
return {
vod_id: v.bvid + '@' + v.aid,
vod_name: decodeHtml(v.title.replace(/<[^>]*>/g, '')),
vod_pic: img,
vod_remarks: remarkParts.join(' | ')
};
});
let hasMore = list.length >= 20;
if (!list.length) list.push({ vod_id: '0', vod_name: '无结果', vod_pic: PLACEHOLDER });
return JSON.stringify({ list, page, pagecount: hasMore ? page + 1 : page, limit: 20, total: list.length });
} catch (e) {
if (retry === 2) {
return JSON.stringify({ list: [{ vod_id: '0', vod_name: '请求失败', vod_pic: PLACEHOLDER }], page: 1, pagecount: 1 });
}
await new Promise(resolve => setTimeout(resolve, 1500));
}
}
}
async function detail(ids) {
let idStr = Array.isArray(ids) ? ids[0] : ids;
if (!idStr || idStr.indexOf('@') === -1) return JSON.stringify({ list: [] });
let parts = idStr.split('@'), aid = parts[1];
try {
let viewUrl = 'https://api.bilibili.com/x/web-interface/view?aid=' + aid;
let resp = await req(viewUrl, { headers: getHeaders(true) });
let info = JSON.parse(resp.content).data;
if (!info) return JSON.stringify({ list: [] });
let title = decodeHtml((info.title || '').replace(/<[^>]*>/g, ''));
let pic = info.pic || PLACEHOLDER;
if (pic.startsWith('//')) pic = 'https:' + pic;
let desc = info.desc || '';
let duration = info.duration || 0;
let owner = info.owner || {};
let stat = info.stat || {};
let tname = info.tname || '';
let pubdate = info.pubdate || 0;
let upName = owner.name || '未知';
let follower = '';
try {
let statUrl = 'https://api.bilibili.com/x/relation/stat?vmid=' + owner.mid;
let statResp = await req(statUrl, { headers: getHeaders(false) });
let statData = JSON.parse(statResp.content).data;
if (statData && statData.follower > 0) {
follower = formatCount(statData.follower);
}
} catch (e) {}
let director = '🆙 ' + '[a=cr:' + JSON.stringify({id: upName + '_clicklink', name: upName}) + '/]' + upName + '[/a]';
if (follower) director += ' 👥 ' + follower;
let actor = '▶' + (stat.view || 0) + ' 💬' + (stat.danmaku || 0) + ' 👍' + (stat.like || 0) + ' 💰' + (stat.coin || 0) + ' ⭐' + (stat.favorite || 0);
let year = pubdate ? new Date(pubdate * 1000).getFullYear().toString() : '';
let pages = info.pages || [];
let bilibiliPlayUrls = pages.map(p => {
let epTitle = (p.part || 'P' + p.page).replaceAll('#', '﹟').replaceAll('$', '﹩');
return epTitle + '$' + aid + '+' + p.cid;
});
let relatedPlayUrls = [];
try {
let relatedUrl = 'https://api.bilibili.com/x/web-interface/archive/related?aid=' + aid;
let relResp = await req(relatedUrl, { headers: getHeaders(true) });
let relData = JSON.parse(relResp.content).data;
if (relData && Array.isArray(relData)) {
relData.forEach(rd => {
let cid = rd.cid;
let rdTitle = decodeHtml((rd.title || '').replaceAll('#', '﹟').replaceAll('$', '﹩'));
let aaid = rd.aid;
relatedPlayUrls.push(rdTitle + '$' + aaid + '+' + cid);
});
}
} catch (e) {}
let playFrom = 'B站';
let playUrl = bilibiliPlayUrls.join('#');
if (relatedPlayUrls.length > 0) {
playFrom += '$$$相关推荐';
playUrl += '$$$' + relatedPlayUrls.join('#');
}
return JSON.stringify({
list: [{
vod_id: idStr,
vod_name: title,
vod_pic: pic,
vod_remarks: Math.floor(duration / 60) + '分钟',
vod_content: desc,
vod_play_from: playFrom,
vod_play_url: playUrl,
vod_director: director,
vod_actor: actor,
type_name: tname,
vod_year: year
}]
});
} catch (e) {
return JSON.stringify({ list: [] });
}
}
async function play(flag, id) {
if (!id || id.indexOf('+') === -1) return JSON.stringify({ parse: 0, url: '' });
let parts = id.split('+'), aid = parts[0], cid = parts[1];
try {
let dashUrl = 'https://api.bilibili.com/x/player/playurl?avid=' + aid + '&cid=' + cid + '&qn=127&fnval=4048&fourk=1';
let resp = await req(dashUrl, { headers: getHeaders(true) });
let data = JSON.parse(resp.content);
if (!data.data || !data.data.dash) return JSON.stringify({ parse: 0, url: '' });
let dash = data.data.dash;
let videos = dash.video || [];
let duration = dash.duration || 0;
let minBuf = dash.min_buffer_time || 1.5;
let rawAudios = dash.audio ? [...dash.audio] : [];
if (dash.flac && dash.flac.audio) {
if (Array.isArray(dash.flac.audio)) rawAudios.push(...dash.flac.audio);
else rawAudios.push(dash.flac.audio);
}
if (dash.dolby && dash.dolby.audio) {
if (Array.isArray(dash.dolby.audio)) rawAudios.push(...dash.dolby.audio);
else rawAudios.push(dash.dolby.audio);
}
let audioList = [];
let seenAudio = new Set();
const audioPriority = { 30251: 1, 30250: 2, 30280: 3, 30232: 4, 30216: 5 };
rawAudios.sort((a, b) => {
let pA = audioPriority[a.id] || 99;
let pB = audioPriority[b.id] || 99;
return pA - pB;
});
for (let a of rawAudios) {
if (a && a.id && !seenAudio.has(a.id)) {
seenAudio.add(a.id);
audioList.push(a);
}
}
let formats = data.data.support_formats || [];
if (formats.length === 0) {
let aq = data.data.accept_quality || [];
let ad = data.data.accept_description || [];
formats = aq.map((q, i) => ({ quality: q, display_desc: ad[i] || q.toString() }));
}
let qnMap = {};
formats.forEach(f => { qnMap[f.quality] = f.display_desc || f.new_description || f.quality.toString(); });
let urlsArray = [];
let seenStreams = new Set();
videos.sort((a, b) => b.id - a.id);
for (let v of videos) {
let qn = v.id;
let codecName = '';
if (v.codecs) {
if (v.codecs.includes('avc')) codecName = 'AVC';
else if (v.codecs.includes('hev') || v.codecs.includes('hvc')) codecName = 'HEVC';
else if (v.codecs.includes('av01')) codecName = 'AV1';
else if (v.codecs.includes('dvh') || v.codecs.includes('dve')) codecName = '杜比';
}
let streamKey;
if (qn <= 80) {
streamKey = qn.toString();
codecName = '';
} else {
streamKey = qn + '_' + codecName;
}
if (!seenStreams.has(streamKey)) {
seenStreams.add(streamKey);
let baseName = qnMap[qn] || ('画质' + qn);
if (qn === 126 || codecName === '杜比') {
baseName = '杜比视界';
} else if (qn === 125) {
baseName = 'HDR真彩色(' + (codecName || 'HEVC') + ')';
} else {
if (codecName) baseName += '(' + codecName + ')';
}
let mpd = buildMpd(v, audioList, duration, minBuf);
let dataUri = 'data:application/dash+xml;base64,' + base64Encode(mpd);
urlsArray.push({ name: baseName, url: dataUri, qn: qn, bw: v.bandwidth || 0 });
}
}
if (urlsArray.length === 0) return JSON.stringify({ parse: 0, url: '' });
urlsArray.sort((a, b) => {
if (b.qn !== a.qn) return b.qn - a.qn;
return b.bw - a.bw;
});
let arr = [];
urlsArray.forEach(q => {
arr.push(q.name);
arr.push(q.url);
});
return JSON.stringify({
parse: 0,
jx: 0,
url: arr,
danmaku: 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + cid,
header: JSON.stringify(getHeaders(true))
});
} catch (e) { return JSON.stringify({ parse: 0, url: '' }); }
}
function buildMpd(video, audioList, duration, minBuf) {
let mpd = '<?xml version="1.0" encoding="utf-8"?>\n';
mpd += '<MPD xmlns="urn:mpeg:dash:schemas:mpd:2011" minBufferTime="PT' + minBuf + 'S" type="static" mediaPresentationDuration="PT' + duration + 'S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">\n';
mpd += ' <Period duration="PT' + duration + 'S">\n';
mpd += ' <AdaptationSet contentType="video" mimeType="' + video.mime_type + '" segmentAlignment="true" startWithSAP="1">\n';
mpd += ' <Representation id="' + video.id + '" bandwidth="' + video.bandwidth + '" codecs="' + video.codecs + '" width="' + video.width + '" height="' + video.height + '" frameRate="' + (video.frame_rate || video.frameRate || 25) + '" sar="' + (video.sar || '1:1') + '">\n';
mpd += ' <BaseURL>' + (video.baseUrl || video.base_url).replace(/&/g, '&amp;') + '</BaseURL>\n';
mpd += ' <SegmentBase indexRange="' + (video.segment_base ? video.segment_base.index_range : '0-0') + '">\n';
mpd += ' <Initialization range="' + (video.segment_base ? video.segment_base.initialization : '0-0') + '"/>\n';
mpd += ' </SegmentBase>\n';
mpd += ' </Representation>\n';
mpd += ' </AdaptationSet>\n';
if (audioList && audioList.length > 0) {
for (let audio of audioList) {
let label = '';
if (audio.id === 30251) label = '无损Hi-Res';
else if (audio.id === 30250) label = '杜比全景声';
else if (audio.id === 30280) label = '192k高音质';
else if (audio.id === 30232) label = '132k标准';
else if (audio.id === 30216) label = '64k流畅';
else label = '音质_' + audio.id;
mpd += ' <AdaptationSet contentType="audio" mimeType="' + audio.mime_type + '" segmentAlignment="true" startWithSAP="0" lang="' + label + '">\n';
mpd += ' <Label>' + label + '</Label>\n';
mpd += ' <Representation id="audio_' + audio.id + '" bandwidth="' + audio.bandwidth + '" codecs="' + audio.codecs + '">\n';
mpd += ' <BaseURL>' + (audio.baseUrl || audio.base_url).replace(/&/g, '&amp;') + '</BaseURL>\n';
mpd += ' <SegmentBase indexRange="' + (audio.segment_base ? audio.segment_base.index_range : '0-0') + '">\n';
mpd += ' <Initialization range="' + (audio.segment_base ? audio.segment_base.initialization : '0-0') + '"/>\n';
mpd += ' </SegmentBase>\n';
mpd += ' </Representation>\n';
mpd += ' </AdaptationSet>\n';
}
}
mpd += ' </Period>\n</MPD>';
return mpd;
}
function base64Encode(str) {
let bytes = [];
for (let i = 0; i < str.length; i++) {
let c = str.charCodeAt(i);
if (c < 0x80) bytes.push(c);
else if (c < 0x800) {
bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
} else if (c < 0x10000) {
bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
} else {
bytes.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
}
}
let base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
let result = '';
for (let i = 0; i < bytes.length; i += 3) {
let b1 = bytes[i], b2 = bytes[i + 1] || 0, b3 = bytes[i + 2] || 0;
result += base64.charAt(b1 >> 2);
result += base64.charAt(((b1 & 0x3) << 4) | (b2 >> 4));
result += (i + 1 < bytes.length) ? base64.charAt(((b2 & 0xf) << 2) | (b3 >> 6)) : '=';
result += (i + 2 < bytes.length) ? base64.charAt(b3 & 0x3f) : '=';
}
return result;
}
async function search(keyword, quick, pg) { return category(keyword, pg || '1', {}, {}); }
async function searchContent(keyword, quick, page) { return search(keyword, quick, page); }
export function __jsEvalReturn() {
return { init, home, homeVod, category, detail, play, search, searchContent };
}
+362
View File
@@ -0,0 +1,362 @@
import { load } from 'assets://js/lib/cat.js';
var BILI_COOKIE = '';
var isLogin = false;
var isVip = false;
function getHeaders(needCookie) {
var cookie = BILI_COOKIE || 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc';
return {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36 Edg/143.0.0.0',
'Referer': 'https://www.bilibili.com',
'Cookie': needCookie ? cookie.replace(/,/g, '%2C') : 'buvid3=84B0395D-C9F2-C490-E92E-A09AB48FE26E71636infoc'
};
}
var FIXED_FILTERS = [
{ name: '排序', key: 'order', value: [
{ v: '0', n: '更新时间' }, { v: '1', n: '弹幕数量' }, { v: '2', n: '播放数量' },
{ v: '3', n: '追看人数' }, { v: '4', n: '最高评分' }, { v: '5', n: '开播时间' }, { v: '6', n: '上映时间' }
]},
{ name: '付费', key: 'season_status', value: [
{ v: '-1', n: '全部' }, { v: '1', n: '免费' }, { v: '2%2C6', n: '付费' }, { v: '4%2C6', n: '大会员' }
]}
];
var ALL_FILTERS = {};
['1','3','4','2','7','5'].forEach(function(t) { ALL_FILTERS[t] = FIXED_FILTERS; });
async function init(config) {
var json = {};
try { json = JSON.parse(config); } catch (e) {}
var cookieSource = json.cookie || ''; // 这里填cookie或外链URL
if (cookieSource) {
if (cookieSource.startsWith('http://') || cookieSource.startsWith('https://')) {
try {
var resp = await req(cookieSource, { headers: { 'User-Agent': 'Mozilla/5.0' }, timeout: 8000 });
if (resp && resp.content) {
BILI_COOKIE = resp.content.trim();
}
} catch (e) { BILI_COOKIE = ''; }
} else {
BILI_COOKIE = cookieSource;
}
}
try {
var navResp = await req('https://api.bilibili.com/x/web-interface/nav', { headers: getHeaders(true) });
var navData = JSON.parse(navResp.content);
if (navData.data) {
isLogin = navData.data.isLogin || false;
isVip = navData.data.vipStatus === 1;
}
} catch (e) {}
return '{}';
}
async function home(filter) {
var classes = [
{ type_id: '1', type_name: '番剧' },
{ type_id: '4', type_name: '国创' },
{ type_id: '2', type_name: '电影' },
{ type_id: '5', type_name: '电视剧' },
{ type_id: '3', type_name: '纪录' },
{ type_id: '7', type_name: '综艺' }
];
return JSON.stringify({ class: classes, filters: ALL_FILTERS });
}
async function homeVod() { return JSON.stringify({ list: [] }); }
async function category(tid, pg, filterParams, extendParams) {
var page = parseInt(pg) || 1;
var typeId = tid;
var params = { order: '0', season_status: '-1', style_id: -1, sort: -1, area: -1 };
var sel = {};
if (typeof filterParams === 'object') Object.assign(sel, filterParams);
if (typeof extendParams === 'object') Object.assign(sel, extendParams);
if (sel.order) params.order = sel.order;
if (sel.season_status) params.season_status = sel.season_status;
var query = 'order=' + params.order + '&season_status=' + params.season_status +
'&style_id=' + params.style_id + '&sort=' + params.sort + '&area=' + params.area +
'&pagesize=20&type=1&season_type=' + typeId + '&page=' + page;
var url = 'https://api.bilibili.com/pgc/season/index/result?' + query;
var resp = await req(url, { headers: getHeaders(false) });
if (!resp || !resp.content) return JSON.stringify({ list: [] });
var data = JSON.parse(resp.content);
var list = [];
var seasons = (data.data && data.data.list) ? data.data.list : [];
seasons.forEach(function(item) {
list.push({
vod_id: item.season_id,
vod_name: item.title.replace(/<[^>]*>/g, ''),
vod_pic: item.cover,
vod_remarks: item.index_show || ''
});
});
var hasMore = data.data && data.data.has_next === 1;
return JSON.stringify({ list: list, page: page, pagecount: hasMore ? page + 1 : page, limit: 20, total: hasMore ? (page+1)*20 : page*20 });
}
async function detail(ids) {
var seasonId = Array.isArray(ids) ? ids[0] : ids;
if (!seasonId) return JSON.stringify({ list: [] });
var detailUrl = 'https://api.bilibili.com/pgc/view/web/season?season_id=' + seasonId;
var resp = await req(detailUrl, { headers: getHeaders(true) });
if (!resp || !resp.content) return JSON.stringify({ list: [] });
var data = JSON.parse(resp.content);
var season = data.result;
if (!season) return JSON.stringify({ list: [] });
// 基础信息
var title = season.title || '未知剧名';
var cover = season.cover || '';
var desc = season.evaluate || '';
var actors = season.actors || '';
var episodes = season.episodes || [];
var typeName = season.share_sub_title || '';
var areas = (season.areas && season.areas[0]) ? season.areas[0].name : '';
var pubDate = (season.publish && season.publish.pub_time) ? season.publish.pub_time.substring(0, 4) : '';
var newEpDesc = (season.new_ep && season.new_ep.desc) ? season.new_ep.desc : '';
var stat = season.stat || {};
var danmakus = stat.danmakus || 0;
var likes = stat.likes || 0;
var coins = stat.coins || 0;
var favorites = stat.favorites || 0;
var scoreText = '';
if (season.rating && season.rating.score) {
scoreText = '评分: ' + season.rating.score + ' ' + (season.subtitle || '');
} else {
scoreText = '暂无评分' + ' ' + (season.subtitle || '');
}
function zh(num) {
if (Number(num) > 1e8) return (num / 1e8).toFixed(2) + '亿';
if (Number(num) > 1e4) return (num / 1e4).toFixed(2) + '万';
return num.toString();
}
var statusText = '弹幕: ' + zh(danmakus) + ' 点赞: ' + zh(likes) + ' 投币: ' + zh(coins) + ' 追番追剧: ' + zh(favorites);
var playUrls = episodes.map(function(ep) {
var part = (ep.share_copy || ep.title || '剧集').replace('#', '\uff03').replace('$', '\uff04');
return part + '$' + ep.aid + '+' + ep.cid;
}).join('#');
return JSON.stringify({
list: [{
vod_id: seasonId,
vod_name: title,
vod_pic: cover,
vod_remarks: episodes.length + '集 ' + (newEpDesc || ''),
vod_content: desc,
vod_actor: actors,
vod_director: scoreText,
vod_area: areas,
type_name: typeName,
vod_year: pubDate,
vod_play_from: '哔哩影视',
vod_play_url: playUrls
}]
});
}
async function search(keyword, quick, pg) {
var page = pg || '1';
var keywordEnc = encodeURIComponent(keyword);
var ftUrl = 'https://api.bilibili.com/x/web-interface/search/type?search_type=media_ft&keyword=' + keywordEnc + '&page=' + page;
var bangumiUrl = 'https://api.bilibili.com/x/web-interface/search/type?search_type=media_bangumi&keyword=' + keywordEnc + '&page=' + page;
var results = [];
try {
var [ftResp, bangumiResp] = await Promise.all([
req(ftUrl, { headers: getHeaders(false) }),
req(bangumiUrl, { headers: getHeaders(false) })
]);
[ftResp, bangumiResp].forEach(function(resp) {
if (!resp || !resp.content) return;
var data = JSON.parse(resp.content);
var items = (data.data && data.data.result) ? data.data.result : [];
items.forEach(function(item) {
results.push({
vod_id: item.season_id,
vod_name: item.title.replace(/<[^>]*>/g, ''),
vod_pic: item.cover,
vod_remarks: item.index_show || ''
});
});
});
} catch (e) {}
var seen = new Set();
var finalList = [];
results.forEach(function(item) { if (!seen.has(item.vod_id)) { seen.add(item.vod_id); finalList.push(item); } });
return JSON.stringify({ list: finalList, page: parseInt(page), pagecount: 1, limit: finalList.length, total: finalList.length });
}
async function play(flag, id) {
if (!id || id.indexOf('+') === -1) return JSON.stringify({ parse: 0, url: '' });
var parts = id.split('+');
var aid = parts[0], cid = parts[1];
// ===== 优先 DASH =====
try {
var dashUrl = 'https://api.bilibili.com/x/player/playurl?avid=' + aid + '&cid=' + cid + '&qn=127&fnval=4048&fnver=0&fourk=1&platform=pc';
var resp = await req(dashUrl, { headers: getHeaders(true), timeout: 10000 });
var json = JSON.parse(resp.content);
var result = json.result || json.data;
if (result && result.dash) {
var dash = result.dash;
var videos = dash.video || [];
var audios = dash.audio || [];
var duration = dash.duration || 0;
var minBuf = dash.min_buffer_time || 1.5;
var formats = result.support_formats || [];
if (formats.length === 0) {
var aq = result.accept_quality || [];
var ad = result.accept_description || [];
formats = aq.map(function(q, i) { return { quality: q, display_desc: ad[i] || q.toString() }; });
}
var qnMap = {};
formats.forEach(function(f) { qnMap[f.quality] = f.display_desc || f.new_description || f.quality.toString(); });
var audio = null;
for (var i = 0; i < audios.length; i++) {
if (!audio || audios[i].id > audio.id) audio = audios[i];
}
var urlList = [];
var seenQn = new Set();
videos.sort(function(a, b) { return b.id - a.id; });
for (var j = 0; j < videos.length; j++) {
var v = videos[j];
var qn = v.id;
if (qnMap[qn] && !seenQn.has(qn)) {
seenQn.add(qn);
var mpd = buildMpd(v, audio, duration, minBuf);
var dataUri = 'data:application/dash+xml;base64,' + base64Encode(mpd);
urlList.push(qnMap[qn], dataUri);
}
}
if (urlList.length > 0) {
return JSON.stringify({
parse: 0, jx: 0,
url: urlList,
danmaku: 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + cid,
header: JSON.stringify(getHeaders(true))
});
}
}
} catch (e) {}
// ===== FLV 后备:利用真实 quality 去重 =====
try {
var infoUrl = 'https://api.bilibili.com/pgc/player/web/playurl?avid=' + aid + '&cid=' + cid + '&qn=0&fnval=1&fourk=1';
var infoResp = await req(infoUrl, { headers: getHeaders(true), timeout: 5000 });
var infoJson = JSON.parse(infoResp.content);
var infoResult = infoJson.result || infoJson.data;
var acceptQuality = (infoResult && infoResult.accept_quality) ? infoResult.accept_quality : [80, 64, 32, 16];
var acceptDesc = (infoResult && infoResult.accept_description) ? infoResult.accept_description : [];
var descMap = {};
for (var i = 0; i < acceptQuality.length; i++) {
descMap[acceptQuality[i]] = acceptDesc[i] || acceptQuality[i].toString();
}
var collected = {}; // key: 真实qn, value: {url, desc}
// 从高到低遍历
acceptQuality.sort(function(a,b){ return b - a; });
for (var i = 0; i < acceptQuality.length; i++) {
var qn = acceptQuality[i];
try {
var flvUrl = 'https://api.bilibili.com/pgc/player/web/playurl?avid=' + aid + '&cid=' + cid + '&qn=' + qn + '&fnval=1&fourk=1';
var flvResp = await req(flvUrl, { headers: getHeaders(true), timeout: 5000 });
var flvJson = JSON.parse(flvResp.content);
var flvResult = flvJson.result || flvJson.data;
if (flvResult && flvResult.durl && flvResult.durl.length > 0) {
// 以服务器返回的实际质量为准
var realQn = flvResult.quality !== undefined ? flvResult.quality : qn;
if (!collected[realQn]) {
var name = descMap[realQn] || flvResult.format || realQn.toString();
collected[realQn] = { url: flvResult.durl[0].url, desc: name };
}
}
} catch (e2) {}
}
var names = [], urls = [];
// 按真实画质从高到低输出
var keys = Object.keys(collected).sort(function(a,b){ return b - a; });
for (var k = 0; k < keys.length; k++) {
var item = collected[keys[k]];
names.push(item.desc);
urls.push(item.url);
}
if (urls.length > 0) {
var urlList = [];
for (var m = 0; m < names.length; m++) {
urlList.push(names[m], urls[m]);
}
return JSON.stringify({
parse: 0, jx: 0,
url: urlList,
danmaku: 'https://api.bilibili.com/x/v1/dm/list.so?oid=' + cid,
header: JSON.stringify(getHeaders(true))
});
}
} catch (e3) {}
return JSON.stringify({ parse: 0, url: '' });
}
function buildMpd(video, audio, duration, minBuf) {
var mpd = '<?xml version="1.0" encoding="utf-8"?>\n';
mpd += '<MPD xmlns="urn:mpeg:dash:schemas:mpd:2011" minBufferTime="PT' + minBuf + 'S" type="static" mediaPresentationDuration="PT' + duration + 'S" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011">\n';
mpd += ' <Period duration="PT' + duration + 'S">\n';
mpd += ' <AdaptationSet contentType="video" mimeType="' + video.mime_type + '" segmentAlignment="true" startWithSAP="1">\n';
mpd += ' <Representation id="' + video.id + '" bandwidth="' + video.bandwidth + '" codecs="' + video.codecs + '" width="' + video.width + '" height="' + video.height + '" frameRate="' + (video.frame_rate || video.frameRate || 25) + '" sar="' + (video.sar || '1:1') + '">\n';
mpd += ' <BaseURL>' + (video.baseUrl || video.base_url || '').replace(/&/g, '&amp;') + '</BaseURL>\n';
mpd += ' <SegmentBase indexRange="' + (video.segment_base ? video.segment_base.index_range : '0-0') + '">\n';
mpd += ' <Initialization range="' + (video.segment_base ? video.segment_base.initialization : '0-0') + '"/>\n';
mpd += ' </SegmentBase>\n';
mpd += ' </Representation>\n';
mpd += ' </AdaptationSet>\n';
if (audio) {
mpd += ' <AdaptationSet contentType="audio" mimeType="' + audio.mime_type + '" segmentAlignment="true" startWithSAP="0">\n';
mpd += ' <Representation id="' + audio.id + '" bandwidth="' + audio.bandwidth + '" codecs="' + audio.codecs + '">\n';
mpd += ' <BaseURL>' + (audio.baseUrl || audio.base_url || '').replace(/&/g, '&amp;') + '</BaseURL>\n';
mpd += ' <SegmentBase indexRange="' + (audio.segment_base ? audio.segment_base.index_range : '0-0') + '">\n';
mpd += ' <Initialization range="' + (audio.segment_base ? audio.segment_base.initialization : '0-0') + '"/>\n';
mpd += ' </SegmentBase>\n';
mpd += ' </Representation>\n';
mpd += ' </AdaptationSet>\n';
}
mpd += ' </Period>\n</MPD>';
return mpd;
}
function base64Encode(str) {
var bytes = [];
for (var i = 0; i < str.length; i++) {
var c = str.charCodeAt(i);
if (c < 0x80) bytes.push(c);
else if (c < 0x800) bytes.push(0xc0 | (c >> 6), 0x80 | (c & 0x3f));
else if (c < 0x10000) bytes.push(0xe0 | (c >> 12), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
else bytes.push(0xf0 | (c >> 18), 0x80 | ((c >> 12) & 0x3f), 0x80 | ((c >> 6) & 0x3f), 0x80 | (c & 0x3f));
}
var base64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var result = '';
for (var i = 0; i < bytes.length; i += 3) {
var b1 = bytes[i], b2 = bytes[i + 1] || 0, b3 = bytes[i + 2] || 0;
result += base64.charAt(b1 >> 2);
result += base64.charAt(((b1 & 0x3) << 4) | (b2 >> 4));
result += (i + 1 < bytes.length) ? base64.charAt(((b2 & 0xf) << 2) | (b3 >> 6)) : '=';
result += (i + 2 < bytes.length) ? base64.charAt(b3 & 0x3f) : '=';
}
return result;
}
export function __jsEvalReturn() {
return { init, home, homeVod, category, detail, search, play };
}