This commit is contained in:
Liu committed 2026-02-05 03:01:51 +08:00
1 parent a06ffa980e
commit e2148d3afc
25 files changed
+12395 -14577

No files matched your search

-2292
View File
File diff suppressed because it is too large. Load diff
+318
View File
@@ -0,0 +1,318 @@
/*
title: '星辰影院', author: '小可乐/v6.1.1'
说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下
"ext": {
"host": "xxxx", //站点网址
"timeout": 6000, //请求超时,单位毫秒
"catesSet": "电视剧&电影&综艺", //指定分类和顺序
"tabsSet": "土星&下载线1" //指定线路和顺序
}
*/
const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
const DefHeader = {'User-Agent': MOBILE_UA};
var HOST;
var KParams = {
headers: {'User-Agent': MOBILE_UA},
timeout: 5000
};
async function init(cfg) {
try {
HOST = (cfg.ext?.host?.trim() || 'https://www.xcyycn.com').replace(/\/$/, '');
KParams.headers['Referer'] = HOST;
let parseTimeout = parseInt(cfg.ext?.timeout?.trim(), 10);
if (parseTimeout > 0) {KParams.timeout = parseTimeout;}
KParams.catesSet = cfg.ext?.catesSet?.trim() || '';
KParams.tabsSet = cfg.ext?.tabsSet?.trim() || '';
KParams.resHtml = await request(HOST);
} catch (e) {
console.error('初始化参数失败:', e.message);
}
}
async function home(filter) {
try {
let resHtml = KParams.resHtml;
if (!resHtml) {throw new Error('源码为空');}
let typeArr = cutStr(resHtml, 'hl-nav-item£>', '</li>', '', false, 0, true).filter(flt => flt.includes('/v/'));
let classes = typeArr.map((it,idx) => {
let cName = cutStr(it, '>', '<', `分类${idx+1}`);
let cId = cutStr(it, '/v/', '.', `值${idx+1}`);
return {type_name: cName, type_id: cId};
});
if (KParams.catesSet) {classes = ctSet(classes, KParams.catesSet);}
let filters = {};
try {
const nameObj = {class: 'class,剧情', area: 'area,地区', lang: 'lang,语言', year: 'year,年份', letter: 'letter,字母', by: 'by,排序'};
const regObj = {class: /\d+---([^-]+)-/, area: /\/\d+-([^-]+)-/, lang: /\d+----([^-]+)-/, year: /-([^-]+)\./, letter: /\d+-----([^-]+)-/, by: /\d+--([^-]+)-/};
let resHtmlList = await Promise.all(
classes.map(async (it) => {
try {return await request(`${HOST}/vs/${it.type_id}-----------.html`);} catch (sErr) {return '';}
})
);
classes.forEach((it,idx) => {
let resfHtml = resHtmlList[idx];
if (resfHtml) {
let flValArr = cutStr(resfHtml, 'hl-filter-list', '</ul>', '', false, 0, true).slice(1);
filters[it.type_id] = Object.entries(nameObj).map(([nObjk, nObjv]) => {
let [kkey, kname] = nObjv.split(',');
let tgVal = flValArr.find(fv => regObj[kkey].test(fv)) ?? '';
if (kkey === 'by') {tgVal = cutStr(resfHtml, 'hl-rb-title', '</div>', '', false);}
let tgValArr = cutStr(tgVal, '<a', '/a>', '', false, 0, true);
let tValArr = kkey !== 'by' ? tgValArr.slice(1) : tgValArr;
let kvalue = tValArr.map(el => {
let n = cutStr(el, '>', '<', '空白');
let v = n;
if (kkey === 'by') {v = el.match(regObj[kkey])?.[1] ?? '';}
return {n: n, v: v};
});
if (kkey !== 'by') {kvalue.unshift({n: '全部', v: ''});}
return {key: kkey, name: kname, value: kvalue};
}).filter(flt => flt.key && flt.value.length > 1);
}
});
} catch (e) {
filters = {}
}
return JSON.stringify({class: classes, filters: filters});
} catch (e) {
console.error('获取分类失败:', e.message);
return JSON.stringify({class: [], filters: {}});
}
}
async function homeVod() {
try {
let resHtml = KParams.resHtml;
let VODS = getVodList(resHtml);
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), pg = pg > 0 ? pg : 1;
let fl = extend || {};
let cateUrl = `${HOST}/vs/${fl.cateId || tid}-${fl.area ?? ''}-${fl.by ?? ''}-${fl.class ?? ''}-${fl.lang ?? ''}-${fl.letter ?? ''}---${pg}---${fl.year ?? ''}.html`;
let resHtml = await request(cateUrl);
let VODS = getVodList(resHtml);
let limit = VODS.length;
let pagecount = cutStr(resHtml, 'hl-page-total£/', '页', '1');
pagecount = Number(pagecount);
return JSON.stringify({list: VODS, page: pg, pagecount: pagecount, limit: limit, total: limit*pagecount});
} 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), pg = pg > 0 ? pg : 1;
let searchUrl = `${HOST}/s${wd}/page/${pg}.html`;
let resHtml = await request(searchUrl);
let 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) {
try {
if (!khtml) {throw new Error('源码为空');}
let kvods = [];
let listArr = cutStr(khtml, 'hl-lazy', '</a>', '', false, 0, true).filter(flt => flt.includes('remarks'));
for (let it of listArr) {
let kname = cutStr(it, 'title="', '"', '名称');
let kpic = cutStr(it, 'data-original="', '"', '图片');
let kremarks = cutStr(it, 'remarks">', '</', '状态');
let kid = cutStr(it, 'href="', '"');
if (kid) {
kvods.push({
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
vod_id: `${kid}@${kname}@${kpic}@${kremarks}`
});
}
}
return kvods;
} catch (e) {
console.error(`生成视频列表失败:`, e.message);
return [];
}
}
async function detail(ids) {
try {
let [id, kname, kpic, kremarks] = ids.split('@');
let detailUrl = !/^http/.test(id) ? `${HOST}${id}` : id;
let resHtml = await request(detailUrl);
if (!resHtml) {throw new Error('源码为空');}
let intros = cutStr(resHtml, '"clearfix">', '</ul>', '', false);
let [ktabs, kurls] = [[], []];
let zx_tabs = cutStr(resHtml, '<span class="hl-from', '/span>', '', false, 0, true).map((it,idx) => cutStr(it, '>', '<', `在线${idx+1}`) );
ktabs.push(...zx_tabs);
let zx_urls = cutStr(resHtml, 'hl-plays-list">', '</ul>', '', false, 0, true).map((item,idx) => cutStr(item, '<li', '</li>', '', false, 0, true).map(it => { return cutStr(it, '>', '</a>', 'noEpi') + '$' + HOST + cutStr(it, 'href="', '"', 'noUrl'); }).join('#') );
kurls.push(...zx_urls);
let xzArr = cutStr(resHtml, 'hl-downs-list hl', '</ul>', '', false, 0, true);
if (xzArr[0]) {
xzArr.forEach((item,idx) => {
let siglUrl = cutStr(item, 'down_url', '>', '', false, 0, true).map(it => `${cutStr(it, 'file_name="', '"', 'noEpi')}$${cutStr(it, 'value="', '"', 'noUrl')}` ).join('#');
ktabs.push(`下载线${idx+1}`);
kurls.push(siglUrl);
});
}
if (KParams.tabsSet) {
let ktus = ktabs.map((it, idx) => { return {type_name: it, type_value: kurls[idx]} });
ktus = ctSet(ktus, KParams.tabsSet);
ktabs = ktus.map(it => it.type_name);
kurls = ktus.map(it => it.type_value);
}
let VOD = {
vod_id: detailUrl,
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
type_name: cutStr(intros, '类型:', '</li>', '类型'),
vod_year: cutStr(intros, '年份:', '</li>', '1000'),
vod_area: cutStr(intros, '地区:', '</li>', '地区'),
vod_lang: cutStr(intros, '语言:', '</li>', '语言'),
vod_director: cutStr(intros, '导演:', '</li>', '导演'),
vod_actor: cutStr(intros, '主演:', '</li>', '主演'),
vod_content: cutStr(intros, '简介:', '</li>', kname),
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: []});
}
}
async function play(flag, ids, flags) {
try {
let kp = 0, kurl = '';
if (/下载/.test(flag)) {
kurl = ids;
} else {
let resHtml = await request(ids);
let codeObj = safeParseJSON(cutStr(resHtml, 'var player_£=', '<', '', false));
kurl = codeObj?.url ?? '';
if (!/^http/.test(kurl)) {
kurl = ids;
kp = 1;
}
}
return JSON.stringify({jx: 0, parse: kp, url: kurl, header: DefHeader});
} catch (e) {
console.error('播放失败:', e.message);
return JSON.stringify({jx: 0, parse: 0, url: '', header: {}});
}
}
function ctSet(kArr, setStr) {
try {
if (!Array.isArray(kArr) || kArr.length === 0 || typeof setStr !== 'string' || !setStr) { throw new Error('第一参数需为非空数组,第二参数需为非空字符串'); }
const set_arr = [...kArr];
const arrNames = setStr.split('&');
const filtered_arr = arrNames.map(item => set_arr.find(it => it.type_name === item)).filter(Boolean);
return filtered_arr.length? filtered_arr : [set_arr[0]];
} catch (e) {
console.error('ctSet 执行异常:', e.message);
return kArr;
}
}
function safeParseJSON(jStr){
try {return JSON.parse(jStr);} catch(e) {return null;}
}
function cutStr(str, prefix = '', suffix = '', defVal = '', clean = true, i = 0, all = false) {
try {
if (typeof str !== 'string') {throw new Error('被截取对象必须为字符串');}
const cleanStr = cs => String(cs).replace(/<[^>]*?>/g, ' ').replace(/(&nbsp;|[\u0020\u00A0\u3000\s])+/g, ' ').trim().replace(/\s+/g, ' ');
const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&');
let pre = esc(prefix).replace(/£/g, '[^]*?'), end = esc(suffix);
const regex = new RegExp(`${pre || '^'}([^]*?)${end || '$'}`, 'g');
const matchIter = str.matchAll(regex);
if (all) {
let matchArr = [...matchIter];
if (!matchArr.length) {return [defVal];}
return matchArr.map(ela => ela[1] !== undefined ? (clean ? cleanStr(ela[1]) : ela[1]) : defVal);
}
const idx = parseInt(i, 10);
if (isNaN(idx)) {throw new Error('序号必须为整数');}
let tgResult, matchIdx = 0;
if (idx >= 0) {
for (let elt of matchIter) {
if (matchIdx++ === idx) {
tgResult = elt[1];
break;
}
}
} else {
let absI = Math.abs(idx), ringBuf = new Array(absI), ringPtr = 0, ringCnt = 0;
for (let elt of matchIter) {
ringBuf[ringPtr] = elt[1];
ringPtr = (ringPtr + 1) % absI;
ringCnt = Math.min(ringCnt + 1, absI);
matchIdx++;
}
tgResult = (matchIdx >= absI && ringCnt > 0) ? ringBuf[ringPtr % ringCnt] : undefined;
}
return tgResult !== undefined ? (clean ? (cleanStr(tgResult) || defVal) : tgResult) : defVal;
} catch (e) {
console.error(`字符串截取错误:`, e.message);
return all ? ['cutErr'] : 'cutErr';
}
}
async function request(reqUrl, options = {}) {
try {
if (typeof reqUrl !== 'string' || !reqUrl.trim()) { throw new Error('reqUrl需为字符串且非空'); }
if (typeof options !== 'object' || Array.isArray(options) || options === null) { throw new Error('options类型需为非null对象'); }
options.method = options.method?.toUpperCase() || 'GET';
if (['GET', 'HEAD'].includes(options.method)) {
delete options.body;
delete options.data;
delete options.postType;
}
let {headers, timeout, ...restOpts} = options;
const optObj = {
headers: (typeof headers === 'object' && !Array.isArray(headers) && headers) ? headers : KParams.headers,
timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout,
...restOpts
};
const res = await req(reqUrl, optObj);
if (options.withHeaders) {
const resHeaders = typeof res.headers === 'object' && !Array.isArray(res.headers) && res.headers ? res.headers : {};
const resWithHeaders = { ...resHeaders, body: res?.content ?? '' };
return JSON.stringify(resWithHeaders);
}
return res?.content ?? '';
} catch (e) {
console.error(`${reqUrl}→请求失败:`, e.message);
return options?.withHeaders ? JSON.stringify({ body: '' }) : '';
}
}
export function __jsEvalReturn() {
return {
init,
home,
homeVod,
category,
search,
detail,
play,
proxy: null
};
}
+250
View File
@@ -0,0 +1,250 @@
/*
title: '月光影视', author: '小可乐/v5.12.1'
说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下
"ext": {
"host": "xxxx", //站点网址
"timeout": 6000 //请求超时,单位毫秒
}
*/
var HOST;
const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
const DefHeader = {'User-Agent': MOBILE_UA};
const KParams = {
headers: {'User-Agent': MOBILE_UA},
timeout: 5000
};
async function init(cfg) {
try {
let host = cfg.ext?.host?.trim() || 'https://www.dzwhs.com';
HOST = host.replace(/\/$/, '');
KParams.headers['Referer'] = HOST;
let 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 {
let resHtml = KParams.resHtml;
if (!resHtml) {throw new Error('源码为空');}
let classes = cutStr(resHtml, '/zwhstp', '/a>', '', false, 1, true).map(it => {
let cName = cutStr(it, '>', '<', '分类名');
let cId = cutStr(it, '/', '.', '分类值');
return {type_name: cName, type_id: cId};
});
let filters = {
"1":[
{"key":"cateId","name":"类型","value":[{"n":"全部","v":"全部"},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"恐怖片","v":"10"},{"n":"剧情片","v":"11"},{"n":"战争片","v":"12"},{"n":"纪录片","v":"13"},{"n":"悬疑片","v":"14"},{"n":"犯罪片","v":"15"},{"n":"奇幻片","v":"16"},{"n":"动画片","v":"31"},{"n":"预告片","v":"32"}]}
],
"2":[
{"key":"cateId","name":"类型","value":[{"n":"全部","v":"全部"},{"n":"国产剧","v":"17"},{"n":"港台剧","v":"18"},{"n":"日韩剧","v":"20"},{"n":"欧美剧","v":"21"},{"n":"海外剧","v":"22"}]}
],
"3":[
{"key":"cateId","name":"类型","value":[{"n":"全部","v":"全部"},{"n":"大陆综艺","v":"23"},{"n":"日韩综艺","v":"24"},{"n":"欧美综艺","v":"25"},{"n":"港台综艺","v":"26"}]}
],
"4":[
{"key":"cateId","name":"类型","value":[{"n":"全部","v":"全部"},{"n":"国产动漫","v":"27"},{"n":"日韩动漫","v":"28"},{"n":"欧美动漫","v":"29"},{"n":"其他动漫","v":"30"}]}
]
};
return JSON.stringify({class: classes, filters: filters});
} catch (e) {
console.error('获取分类失败:', e.message);
return JSON.stringify({class: [], filters: {}});
}
}
async function homeVod() {
try {
let resHtml = KParams.resHtml;
let VODS = getVodList(resHtml);
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);
pg = pg > 0 ? pg : 1;
let cateUrl = `${HOST}/zwhstp/${extend?.cateId ?? tid}-${pg}.html`;
let resHtml = await request(cateUrl);
let VODS = getVodList(resHtml);
let pagecount = 999;
return JSON.stringify({list: VODS, page: pg, pagecount: pagecount, limit: 30, total: 30*pagecount});
} 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);
pg = pg > 0 ? pg : 1;
let searchUrl = `${HOST}/zwhstp/id.html?wd=${wd}&page=${pg}`;
let resHtml = await request(searchUrl);
let 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) {
try {
if (!khtml) {throw new Error('源码为空');}
let kvods = [];
let listArr = cutStr(khtml, 'lazyload"', '/a>', '', false, 1, true);
for (let it of listArr) {
let kname = cutStr(it, 'title="', '"', '名称');
let kpic = cutStr(it, 'data-original="', '"', '图片');
let kremarks = cutStr(it, 'text-right">', '<', '状态');
kvods.push({
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
vod_id: `${cutStr(it, 'href="', '"', 'Id')}@${kname}@${kpic}@${kremarks}`
});
}
return kvods;
} catch (e) {
console.error(`生成视频列表失败:`, e.message);
return [];
}
}
async function detail(ids) {
try {
let [id, kname, kpic, kremarks] = ids.split('@');
let detailUrl = !/^http/.test(id) ? `${HOST}${id}` : id;
let resHtml = await request(detailUrl);
if (!resHtml) {throw new Error('源码为空');}
let intros = cutStr(resHtml, 'stui-content col-pd', 'play-btn', '', false);
let ktabs = cutStr(resHtml, 'pull-right"></span>', '/h3>', '', false, 1, true).map((it,idx) => cutStr(it, 'title">', '<', `线-${idx+1}`));
let kurls = cutStr(resHtml, '"stui-content__playlist', '</ul>', '', false, 1, true).map(item => {
let kurl = cutStr(item, '<a', '/a>', '', false, 1, true).map(it => { return cutStr(it, '>', '<', 'noEpi') + '$' + cutStr(it, 'href="', '"', 'noUrl'); });
return kurl.join('#');
});
let VOD = {
vod_id: detailUrl,
vod_name: kname,
vod_pic: kpic,
type_name: cutStr(intros, '类型:', '<span', '类型'),
vod_remarks: kremarks,
vod_year: cutStr(intros, '年份:', '</p>', '1000'),
vod_area: cutStr(intros, '地区:', '<span', '地区'),
vod_lang: '语言',
vod_director: cutStr(intros, '导演:', '</p>', '导演'),
vod_actor: cutStr(intros, '主演:', '</p>', '主演'),
vod_content: cutStr(intros, 'detail-content£>', '</span>', '简介'),
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: []});
}
}
async function play(flag, ids, flags) {
try {
let playUrl = !/^http/.test(ids) ? `${HOST}${ids}` : ids;
let kp = 0;
let resHtml = await request(playUrl);
let kcode = safeParseJSON(cutStr(resHtml, 'var player_£=', '<', '', false));
let kurl = kcode?.url ?? '';
if (!/m3u8|mp4|mkv/.test(kurl)) {
kp = 1;
kurl = playUrl;
}
return JSON.stringify({jx: 0, parse: kp, url: kurl, header: DefHeader});
} catch (e) {
console.error('播放失败:', e.message);
return JSON.stringify({jx: 0, parse: 0, url: '', header: {}});
}
}
function cutStr(str, prefix = '', suffix = '', defaultVal = 'cutFaile', clean = true, i = 1, all = false) {
try {
if (!str || typeof str !== 'string') {throw new Error('被截取对象需为非空字符串');}
const dealStr = ds => String(ds).replace(/<[^>]*?>/g, ' ').replace(/(&nbsp;|\u00A0|\s)+/g, ' ').trim().replace(/\s+/g, ' ');
const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&');
let pre = esc(prefix).replace(/£/g, '[^]*?');
let end = esc(suffix);
let regex = new RegExp(`${pre ? pre : '^'}([^]*?)${end ? end : '$'}`, 'g');
let matchArr = [...str.matchAll(regex)];
if (all) {
return matchArr.map(it => {
const val = it[1] ?? defaultVal;
return clean && val !== defaultVal ? dealStr(val) : val;
});
}
i = parseInt(i, 10);
if (isNaN(i) || i < 1) {throw new Error('序号需为大于0的整数');}
i = i - 1;
if (i >= matchArr.length) {throw new Error('序号越界');}
let result = matchArr[i][1] ?? defaultVal;
return clean && result !== defaultVal ? dealStr(result) : result;
} 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, charset, toBase64 = false, ...restOpts } = options;
const optObj = {
headers: (typeof headers === 'object' && !Array.isArray(headers) && headers) ? headers : KParams.headers,
timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout,
charset: charset?.toLowerCase() || 'utf-8',
buffer: toBase64 ? 2 : 0,
...restOpts
};
const res = await req(reqUrl, optObj);
if (options.withHeaders) {
const resHeaders = typeof res.headers === 'object' && !Array.isArray(res.headers) && res.headers ? res.headers : {};
const resWithHeaders = { ...resHeaders, body: res?.content ?? '' };
return JSON.stringify(resWithHeaders);
}
return res?.content ?? '';
} catch (e) {
console.error(`${reqUrl}→请求失败:`, e.message);
return options?.withHeaders ? JSON.stringify({ body: '' }) : '';
}
}
export function __jsEvalReturn() {
return {
init: init,
home: home,
homeVod: homeVod,
category: category,
search: search,
detail: detail,
play: play,
proxy: null
};
}
+360
View File
@@ -0,0 +1,360 @@
/*
title: '爱看机器人', author: '小可乐/v6.1.2'
说明:可以不写ext,也可以写ext,ext支持的参数和格式参数如下
"ext": {
"host": "xxxx", //站点网址
"timeout": 6000, //请求超时,单位毫秒
"tabsSet": "线路2&线路1" //指定线路和顺序
"tabsDeal": "量子&非凡@天堂>360#极速<新浪@优质>>优质无广&暴风>>暴风无广" //线路处理,优先级低于tabsSet,功能依次为"删除@排序@改名",功能用@分隔,优先级从高到低,排序中的#号表示反向排序从此开始,不理解自己试试就明白了
}
*/
const MOBILE_UA = 'Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36';
const DefHeader = {'User-Agent': MOBILE_UA};
var HOST;
var KParams = {
headers: {'User-Agent': MOBILE_UA},
timeout: 8000,
};
async function init(cfg) {
try {
HOST = (cfg.ext?.host?.trim() || 'https://v.aikanbot.com').replace(/\/$/, '');
KParams.headers['Referer'] = HOST;
let parseTimeout = parseInt(cfg.ext?.timeout?.trim(), 10);
if (parseTimeout > 0) {KParams.timeout = parseTimeout;}
KParams.tabsSet = cfg.ext?.tabsSet?.trim() || '';
KParams.tabsDeal = cfg.ext?.tabsDeal?.trim() || '';
} catch (e) {
console.error('初始化参数失败:', e.message);
}
}
async function home(filter) {
try {
let kclassName = '电影$movie&剧集$tv&榜单$billboard';
let classes = kclassName.split('&').map(it => {
let [cName, cId] = it.split('$');
return {type_name: cName, type_id: cId};
});
let filters = {
"movie": [
{"key": "class","name": "剧情","value": [{"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": "豆瓣top250","v": "豆瓣top250"}]}
],
"tv": [
{"key": "class","name": "剧情","value": [{"n": "热门","v": "热门"}, {"n": "美剧","v": "美剧"}, {"n": "英剧","v": "英剧"}, {"n": "韩剧","v": "韩剧"}, {"n": "日剧","v": "日剧"}, {"n": "国产剧","v": "国产剧"}, {"n": "港剧","v": "港剧"}, {"n": "日本动画","v": "日本动画"}, {"n": "综艺","v": "综艺"}, {"n": "纪录片","v": "纪录片"}]}
]
};
return JSON.stringify({class: classes, filters: filters});
} catch (e) {
console.error('获取分类失败:', e.message);
return JSON.stringify({class: [], filters: {}});
}
}
async function homeVod() {
try {
let resHtml = await request(HOST);
let VODS = getVodList(resHtml);
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), pg = pg > 0 ? pg : 1;
let fl = extend || {};
let suffix = pg === 1 ? '' : `-p-${pg}`;
let cateUrl = tid === 'billboard' ? `${HOST}/billboard.html` : `${HOST}/hot/index-${tid}-${fl.class || '热门'}${suffix}.html`;
let resHtml = await request(cateUrl);
let VODS = getVodList(resHtml);
let limit = VODS.length;
let hasMore = cutStr(resHtml, 'page-more">', '</div>').includes('下一页');
let pagecount = hasMore ? pg + 1 : pg;
return JSON.stringify({list: VODS, page: pg, pagecount: pagecount, limit: limit, total: limit*pagecount});
} 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), pg = pg > 0 ? pg : 1;
let suffix = pg === 1 ? '' : `&p=${pg}`;
let searchUrl = `${HOST}/search?q=${wd}${suffix}`;
let resHtml = await request(searchUrl);
let VODS = getVodList(resHtml, true);
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, sch = false) {
try {
if (!khtml) {throw new Error('源码为空');}
let kvods = [];
let listArr = sch ? cutStr(khtml, 'media">', '</h5>', '', false, 0, true) : cutStr(khtml, '<a', '/a>', '', false, 0, true).filter(flt => flt.includes('alt='));
let picProxy = 'https://img-p.aikanbot.com/proxy?url=';
for (let it of listArr) {
let kname = sch ? cutStr(it, 'title-text">', '<', '名称') : cutStr(it, 'alt="', '"', '名称');
let kpic = `${picProxy}${cutStr(it, 'data-src="', '"', '图片')}`;
let kremarks = sch ? cutStr(it, '[', ']', '状态') : '无状态';
let kid = cutStr(it, 'href="', '"');
if (kid) {
kvods.push({
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
vod_id: `${kid}@${kname}@${kpic}@${kremarks}`
});
}
}
return kvods;
} catch (e) {
console.error(`生成视频列表失败:`, e.message);
return [];
}
}
async function detail(ids) {
try {
let [id, kname, kpic, kremarks] = ids.split('@');
let detailUrl = !/^http/.test(id) ? `${HOST}${id}` : id;
let resHtml = await request(detailUrl);
if (!resHtml) {throw new Error('源码为空');}
const tabName = {"dyttm3u8":"天堂","360zy":"360","iqym3u8":"爱奇艺","mtm3u8":"茅台","subm3u8":"速播","nnm3u8":"牛牛","okm3u8":"欧克","tym3u8":"TY","yym3u8":"歪歪","bfzym3u8":"暴风","1080zyk":"优质","kuaikan":"快看","lzm3u8":"量子","ffm3u8":"非凡","snm3u8":"索尼","qhm3u8":"奇虎","hym3u8":"虎牙","haiwaikan":"海外看","gsm3u8":"光速","zuidam3u8":"最大","bjm3u8":"八戒","wolong":"卧龙","xlm3u8":"新浪","yhm3u8":"樱花","tkm3u8":"天空","jsm3u8":"极速","wjm3u8":"无尽","sdm3u8":"闪电","kcm3u8":"快车","jinyingm3u8":"金鹰","fsm3u8":"飞速","tpm3u8":"淘片","lem3u8":"鱼乐","dbm3u8":"百度","tomm3u8":"番茄","ukm3u8":"优酷","ikm3u8":"爱坤","hnzym3u8":"红牛资源","hnm3u8":"红牛","68zy_m3u8":"六八","kdm3u8":"酷点","bdxm3u8":"北斗星","hhm3u8":"豪华","kbm3u8":"快播","mzm3u8":"MZ"};
let [kdirector='', kactors=''] = cutStr(resHtml, '<h3£>', '</h3>', '', true, -1).split('/');
let kid= cutStr(resHtml, 'current_id"£"', '"');
let eToken= cutStr(resHtml, 'e_token"£"', '"');
const getToken = (curtId, eToken) => {
if (!curtId || !eToken || !/^\d+$/.test(curtId)) {return '';}
let remainEToken = eToken;
let finalToken = curtId.slice(-4).split('').map(it => {
let startPos = (parseInt(it, 10) % 3) + 1;
let endPos = startPos + 8;
if (startPos >= remainEToken.length) {return '';}
let segment = remainEToken.substring(startPos, endPos);
remainEToken = remainEToken.substring(endPos);
return segment;
}).filter(Boolean).join('');
return finalToken;
};
let token = getToken(kid, eToken);
let detailUrl2 = `${HOST}/api/getResN?videoId=${kid}&mtype=2&token=${token}`;
let resObj = safeParseJSON(await request(detailUrl2));
let resList = resObj?.data?.list ?? [];
let [ktabs, kurls] = [[], []];
resList.forEach((item, idx) => {
let resData = safeParseJSON(item.resData.replace(/#{2,}/g, '#'))?.[0] ?? {};
let tab = resData?.flag || `线路${idx+1}`;
ktabs.push(tabName[tab] || tab);
kurls.push(resData?.url || 'noUrl');
});
ktabs = dealSameEle(ktabs);
if (KParams.tabsSet || KParams.tabsDeal) {
let ktus = ktabs.map((it, idx) => { return {type_name: it, type_value: kurls[idx]} });
if (KParams.tabsSet) {
ktus = ctSet(ktus, KParams.tabsSet);
}
if (KParams.tabsDeal) {
let [x = '', y = '', z = ''] = KParams.tabsDeal.split('@');
let tab_remove = ['','&'].includes(x.trim()) ? '' : x.trim();
let tab_order = ['','#'].includes(y.trim()) ? '' : y.trim();
let tab_rename = ['','&'].includes(z.trim()) ? '' : z.trim();
ktus = dorDeal(ktus, tab_remove, tab_order, tab_rename);
}
ktabs = ktus.map(it => it.type_name);
kurls = ktus.map(it => it.type_value);
}
let VOD = {
vod_id: kid,
vod_name: kname,
vod_pic: kpic,
vod_remarks: kremarks,
type_name: '类型',
vod_year: cutStr(resHtml, '<h3£>', '</h3>', '1000', true, -3),
vod_area: cutStr(resHtml, '<h3£>', '</h3>', '地区', true, -2),
vod_lang: '语言',
vod_director: kdirector || '导演',
vod_actor: kactors || '主演',
vod_content: cutStr(resHtml, 'description" content="', '">', kname),
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: []});
}
}
async function play(flag, ids, flags) {
try {
let kp = /\.(m3u8|mp4|mkv)/.test(ids) ? 0 : 1;
return JSON.stringify({jx: 0, parse: kp, url: ids, header: DefHeader});
} catch (e) {
console.error('播放失败:', e.message);
return JSON.stringify({jx: 0, parse: 0, url: '', header: {}});
}
}
function dealSameEle(arr) {
try {
if (!Array.isArray(arr)) {throw new Error('输入参数非数组');}
const countMap = new Map();
let newArr = arr.map(item => {
let count = countMap.get(item) || 0;
let currentCount = count + 1;
countMap.set(item, currentCount);
return currentCount > 1 ? `${item}${currentCount}` : item;
});
return newArr;
} catch (e) {
return [];
}
}
function ctSet(kArr, setStr) {
try {
if (!Array.isArray(kArr) || kArr.length === 0 || typeof setStr !== 'string' || !setStr) { throw new Error('第一参数需为非空数组,第二参数需为非空字符串'); }
const set_arr = [...kArr];
const arrNames = setStr.split('&');
const filtered_arr = arrNames.map(item => set_arr.find(it => it.type_name === item)).filter(Boolean);
return filtered_arr.length? filtered_arr : [set_arr[0]];
} catch (e) {
console.error('ctSet 执行异常:', e.message);
return kArr;
}
}
function dorDeal(kArr, strRemove, strOrder, strRename) {
let dealed_arr = [...kArr];
if (strRemove) {
try {
let removeArr = strRemove.split('&');
const removeSet = new Set(removeArr);
let filtered_arr = dealed_arr.filter(it => !removeSet.has(it.type_name));
let retained_arr = filtered_arr.length ? filtered_arr : [dealed_arr[0]];
dealed_arr = retained_arr;
} catch (e) {
console.error('删除失败:', e.message);
}
}
if (strOrder) {
try {
let [a = '', b = ''] = strOrder.split('#', 2);
let arrA = a.split('>').filter(Boolean);
let arrB = b.split('<').filter(Boolean);
let uqArrB = arrB.filter(it => !arrA.includes(it));
let twMap = new Map();
arrA.forEach((item, idx) => {twMap.set(item, { weight: 1, index: idx }); });
uqArrB.forEach((item, idx) => {twMap.set(item, { weight: 3, index: idx }); });
dealed_arr.forEach((it, idx) => { if (!twMap.has(it.type_name)) {twMap.set(it.type_name, { weight: 2, index: idx });} });
let ordered_arr = [...dealed_arr].sort((a, b) => {
let { weight: ta = 2, index: idxA = 0 } = twMap.get(a.type_name) ?? {};
let { weight: tb = 2, index: idxB = 0 } = twMap.get(b.type_name) ?? {};
if (ta !== tb) {return ta - tb;}
return ta === 3 ? idxB - idxA : idxA - idxB;
});
dealed_arr = ordered_arr;
} catch (e) {
console.error('排序失败:', e.message);
}
}
if (strRename) {
try {
const objRename = strRename.split('&').reduce((obj, p) => {
let [k = '', v = ''] = p.split('>>', 2);
if (k + v) {obj[k] = v;}
return obj;
}, {});
let renamed_arr = dealed_arr.map(it => { return { ...it, type_name: objRename[it.type_name] || it.type_name } });
dealed_arr = renamed_arr;
} catch (e) {
console.error('改名失败:', e.message);
}
}
return dealed_arr;
}
function safeParseJSON(jStr){
try {return JSON.parse(jStr);} catch(e) {return null;}
}
function cutStr(str, prefix = '', suffix = '', defVal = '', clean = true, i = 0, all = false) {
try {
if (typeof str !== 'string') {throw new Error('被截取对象必须为字符串');}
const cleanStr = cs => String(cs).replace(/<[^>]*?>/g, ' ').replace(/(&nbsp;|[\u0020\u00A0\u3000\s])+/g, ' ').trim().replace(/\s+/g, ' ');
const esc = s => String(s).replace(/[.*+?${}()|[\]\\/^]/g, '\\$&');
let pre = esc(prefix).replace(/£/g, '[^]*?'), end = esc(suffix);
const regex = new RegExp(`${pre || '^'}([^]*?)${end || '$'}`, 'g');
const matchIter = str.matchAll(regex);
if (all) {
const matchArr = [...matchIter];
return matchArr.length ? matchArr.map(ela => ela[1] !== undefined ? (clean ? (cleanStr(ela[1]) || defVal) : ela[1]) : defVal ) : [defVal];
}
const idx = parseInt(i, 10);
if (isNaN(idx)) {throw new Error('序号必须为整数');}
let tgResult, matchIdx = 0;
if (idx >= 0) {
for (let elt of matchIter) {
if (matchIdx++ === idx) {tgResult = elt[1]; break;}
}
} else {
const matchArr = [...matchIter];
tgResult = matchArr.length ? matchArr[matchArr.length + idx]?.[1] : undefined;
}
return tgResult !== undefined ? (clean ? (cleanStr(tgResult) || defVal) : tgResult) : defVal;
} catch (e) {
console.error(`字符串截取错误:`, e.message);
return all ? ['cutErr'] : 'cutErr';
}
}
async function request(reqUrl, options = {}) {
try {
if (typeof reqUrl !== 'string' || !reqUrl.trim()) { throw new Error('reqUrl需为字符串且非空'); }
if (typeof options !== 'object' || Array.isArray(options) || options === null) { throw new Error('options类型需为非null对象'); }
options.method = options.method?.toUpperCase() || 'GET';
if (['GET', 'HEAD'].includes(options.method)) {
delete options.body;
delete options.data;
delete options.postType;
}
let {headers, timeout, ...restOpts} = options;
const optObj = {
headers: (typeof headers === 'object' && !Array.isArray(headers) && headers) ? headers : KParams.headers,
timeout: parseInt(timeout, 10) > 0 ? parseInt(timeout, 10) : KParams.timeout,
...restOpts
};
const res = await req(reqUrl, optObj);
if (options.withHeaders) {
const resHeaders = typeof res.headers === 'object' && !Array.isArray(res.headers) && res.headers ? res.headers : {};
const resWithHeaders = { ...resHeaders, body: res?.content ?? '' };
return JSON.stringify(resWithHeaders);
}
return res?.content ?? '';
} catch (e) {
console.error(`${reqUrl}→请求失败:`, e.message);
return options?.withHeaders ? JSON.stringify({ body: '' }) : '';
}
}
export function __jsEvalReturn() {
return {
init,
home,
homeVod,
category,
search,
detail,
play,
proxy: null
};
}
+772
View File
@@ -0,0 +1,772 @@
/**
title: "耐看影视",
author: "",
logo: "https://nkvod.org/upload/site/20241223-1/a9ca623af581c93ed3c2f3e98e3a5d4b.png",
more: {
sourceTag: "在线播放"
}
*/
import { Crypto, load, _ } from 'assets://js/lib/cat.js';
let HOST = 'https://nkvod.org';
let siteKey = "", siteType = "", sourceKey = "", ext = "";
let searchCookie = '';
const UA = 'Mozilla/5.0 (Linux; Android 12; ALN-AL00 Build/HUAWEIALN-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Mobile Safari/537.36';
async function getCaptchaBinaryData(url) {
try {
const res = await req(url, {
headers: {
'User-Agent': UA,
'Referer': HOST
},
buffer: 1
});
let binaryData;
if (Array.isArray(res.content)) {
binaryData = new Uint8Array(res.content);
} else {
binaryData = res.content;
}
return {
headers: res.headers,
binaryData: binaryData
};
} catch (error) {
throw error;
}
}
async function request(reqUrl, options = {}) {
const defaultOptions = {
method: 'GET',
headers: {
'User-Agent': UA,
'Referer': HOST
}
};
if (searchCookie) {
defaultOptions.headers['Cookie'] = searchCookie;
}
const mergedOptions = {...defaultOptions, ...options};
let res = await req(reqUrl, mergedOptions);
return res.content;
}
async function requestWithCaptcha(reqUrl, options = {}) {
let verifyType = 'search';
if (typeof options === 'string') {
verifyType = options;
options = {};
} else if (options.verifyType) {
verifyType = options.verifyType;
}
let html = await request(reqUrl, options);
const needCaptcha = html && (
html.includes('系统安全验证') ||
html.includes('请输入验证码') ||
html.includes('verify/index.html') ||
html.includes('verify_check')
);
if (needCaptcha) {
searchCookie = '';
await fetchCk(verifyType);
if (searchCookie) {
html = await request(reqUrl, {
...options,
headers: {
...options.headers,
'Cookie': searchCookie
}
});
}
const stillNeedCaptcha = html && (
html.includes('系统安全验证') ||
html.includes('请输入验证码')
);
if (stillNeedCaptcha) {
return null;
}
}
return html;
}
async function fetchCk(verifyType = 'search') {
try {
const yzm = HOST + "/index.php/verify/index.html?" + Date.now();
const yzmResponse = await getCaptchaBinaryData(yzm);
let cookie = "";
const headers = yzmResponse.headers;
for (const key in headers) {
if (key.toLowerCase() === "set-cookie") {
const cookieValue = headers[key];
if (Array.isArray(cookieValue)) {
cookie = cookieValue[0].split(";")[0];
} else if (typeof cookieValue === 'string') {
cookie = cookieValue.split(";")[0];
}
break;
}
}
if (!cookie) {
return;
}
let base64Image;
if (yzmResponse.binaryData instanceof Uint8Array) {
const wordArray = Crypto.lib.WordArray.create(yzmResponse.binaryData);
base64Image = Crypto.enc.Base64.stringify(wordArray);
} else {
return;
}
if (!base64Image) {
return;
}
try {
const ocrResponse = await req("https://api.nn.ci/ocr/b64/text", {
method: 'POST',
headers: {
'Content-Type': 'text/plain',
'User-Agent': UA,
'Accept': 'application/json'
},
body: base64Image,
timeout: 10000
});
if (!ocrResponse || !ocrResponse.content) {
return;
}
let verifyCode = ocrResponse.content.trim();
if (!verifyCode) {
return;
}
verifyCode = verifyCode.replace(/[^a-zA-Z0-9]/g, '');
if (verifyCode.length < 4) {
return;
}
const submit_url = `${HOST}/index.php/ajax/verify_check?type=${verifyType}&verify=${verifyCode}`;
const submitResponse = await req(submit_url, {
method: 'POST',
headers: {
'User-Agent': UA,
'X-Requested-With': 'XMLHttpRequest',
'Origin': HOST,
'Referer': HOST + '/',
'Cookie': cookie,
'Content-Type': 'application/x-www-form-urlencoded'
},
body: ''
});
if (submitResponse && submitResponse.content) {
try {
const submitJson = JSON.parse(submitResponse.content);
if (submitJson && submitJson.code === 1) {
searchCookie = cookie;
}
} catch (parseError) {
if (submitResponse.content.includes('"code":1') || submitResponse.content.includes('验证成功')) {
searchCookie = cookie;
}
}
}
} catch (ocrError) {
}
} catch (error) {
}
}
function init(cfg) {
siteKey = cfg.skey;
siteType = cfg.stype;
sourceKey = cfg.sourceKey;
ext = cfg.ext;
if (ext && ext.indexOf('http') == 0) {
HOST = ext;
}
}
// 辅助函数:获取元素文本内容(兼容 flutter_js 的 cheerio)
// flutter_js 的 cheerio 实现中 .text() 方法可能返回空,需要使用 .html() 作为备用
function getText($elem) {
if (!$elem || !$elem.length) return '';
// 优先使用 text(),如果返回空则尝试从 html() 提取
let text = '';
try {
text = $elem.text();
} catch (e) {}
if (!text || text.trim() === '') {
try {
// 从 html 中去除标签获取纯文本
const html = $elem.html();
if (html) {
text = html.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ');
}
} catch (e) {}
}
return (text || '').trim();
}
async function home(filter) {
const classes = [
{ type_id: "1", type_name: "电影" },
{ type_id: "2", type_name: "剧集" },
{ type_id: "4", type_name: "动漫" },
{ type_id: "3", type_name: "综艺" }
];
const filterObj = {
"1":[{"key":"class","name":"剧情","value":[{"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":"area","name":"地区","value":[{"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":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"法语","v":"法语"},{"n":"德语","v":"德语"},{"n":"其它","v":"其它"}]},{"key":"letter","name":"字母","value":[{"n":"字母","v":""},{"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":"G","v":"G"},{"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"},{"n":"0-9","v":"0-9"}]},{"key":"by","name":"排序","value":[{"n":"时间排序","v":"time"},{"n":"人气排序","v":"hits"},{"n":"评分排序","v":"score"}]}],
"2":[{"key":"class","name":"剧情","value":[{"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":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"内地","v":"内地"},{"n":"韩国","v":"韩国"},{"n":"香港","v":"香港"},{"n":"台湾","v":"台湾"},{"n":"日本","v":"日本"},{"n":"美国","v":"美国"},{"n":"泰国","v":"泰国"},{"n":"英国","v":"英国"},{"n":"新加坡","v":"新加坡"},{"n":"其他","v":"其他"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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":"2006","v":"2006"},{"n":"2005","v":"2005"},{"n":"2004","v":"2004"}]},{"key":"letter","name":"字母","value":[{"n":"字母","v":""},{"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":"G","v":"G"},{"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"},{"n":"0-9","v":"0-9"}]},{"key":"by","name":"排序","value":[{"n":"时间排序","v":"time"},{"n":"人气排序","v":"hits"},{"n":"评分排序","v":"score"}]}],
"3":[{"key":"class","name":"剧情","value":[{"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":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"内地","v":"内地"},{"n":"港台","v":"港台"},{"n":"日韩","v":"日韩"},{"n":"欧美","v":"欧美"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"letter","name":"字母","value":[{"n":"字母","v":""},{"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":"G","v":"G"},{"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"},{"n":"0-9","v":"0-9"}]},{"key":"by","name":"排序","value":[{"n":"时间排序","v":"time"},{"n":"人气排序","v":"hits"},{"n":"评分排序","v":"score"}]}],
"4":[{"key":"class","name":"剧情","value":[{"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":"area","name":"地区","value":[{"n":"全部","v":""},{"n":"国产","v":"国产"},{"n":"日本","v":"日本"},{"n":"欧美","v":"欧美"},{"n":"其他","v":"其他"}]},{"key":"lang","name":"语言","value":[{"n":"全部","v":""},{"n":"国语","v":"国语"},{"n":"英语","v":"英语"},{"n":"粤语","v":"粤语"},{"n":"闽南语","v":"闽南语"},{"n":"韩语","v":"韩语"},{"n":"日语","v":"日语"},{"n":"其它","v":"其它"}]},{"key":"year","name":"年份","value":[{"n":"全部","v":""},{"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"}]},{"key":"letter","name":"字母","value":[{"n":"字母","v":""},{"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":"G","v":"G"},{"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"},{"n":"0-9","v":"0-9"}]},{"key":"by","name":"排序","value":[{"n":"时间排序","v":"time"},{"n":"人气排序","v":"hits"},{"n":"评分排序","v":"score"}]}]}
return JSON.stringify({
class: classes,
filters: filterObj
});
}
async function homeVod() {
const link = `${HOST}/`;
try {
const html = await request(link);
if (!html) return null;
const $ = load(html);
const hotSection = $('.title-a:contains("热播推荐")').closest('.box-width');
if (hotSection.length === 0) return null;
const items = hotSection.find('.public-list-box');
if (items.length === 0) return null;
const videos = _.map(items, (item) => {
const $item = $(item);
const $link = $item.find('.public-list-exp');
const $img = $item.find('.gen-movie-img');
const $title = $item.find('.time-title');
const $subtitle = $item.find('.public-list-subtitle');
const href = $link.attr('href');
const title = $title.attr('title') || getText($title);
const imgSrc = $img.attr('data-src');
const remarks = getText($subtitle);
const vod_id = href && href.startsWith('http') ? href : HOST + (href || '');
const vod_name = title;
const vod_pic = imgSrc ? (imgSrc.startsWith('http') ? imgSrc : HOST + imgSrc) : '';
const vod_remarks = remarks;
return {
vod_id,
vod_name,
vod_pic,
vod_remarks
};
});
return JSON.stringify({
list: videos
});
} catch (error) {
return JSON.stringify({
list: []
});
}
}
async function category(tid, pg, filter, extend) {
if (pg <= 0) pg = 1;
const homeFilter = {};
const homeResult = JSON.parse(await home(homeFilter));
const filterObj = homeResult.filters;
if (!filterObj) return null;
const filterGroup = filterObj[tid];
if (!filterGroup) return null;
const area = getFilterValue(filterGroup, 'area', extend);
const by = getFilterValue(filterGroup, 'by', extend);
const classValue = getFilterValue(filterGroup, 'class', extend);
const lang = getFilterValue(filterGroup, 'lang', extend);
const letter = getFilterValue(filterGroup, 'letter', extend);
const year = getFilterValue(filterGroup, 'year', extend);
const cateId = getFilterValue(filterGroup, 'cateId', extend);
const baseUrl = `${HOST}/show/${tid}`;
const link = `${baseUrl}-${area}-${by}-${classValue}-${lang}-${letter}---${pg}---${year}.html`;
try {
const html = await requestWithCaptcha(link, 'show');
if (!html) return null;
const $ = load(html);
const isParsedSuccessfully = $('.public-list-box').length > 0;
if (!isParsedSuccessfully) return null;
const items = $('.public-list-box');
const videos = _.map(items, (item) => {
const $item = $(item);
const $link = $item.find('.public-list-exp');
const $title = $item.find('.time-title');
const $img = $item.find('.gen-movie-img');
const $remarks = $item.find('.public-list-subtitle');
const href = $link.attr('href');
const title = $title.attr('title') || getText($title);
const imgSrc = $img.attr('data-src');
const remarks = getText($remarks);
const vod_id = href && href.startsWith('http') ? href : HOST + (href || '');
const vod_name = title;
const vod_pic = imgSrc ? (imgSrc.startsWith('http') ? imgSrc : HOST + imgSrc) : '';
const vod_remarks = remarks;
return {
'vod_id': vod_id,
'vod_name': vod_name,
'vod_pic': vod_pic,
'vod_remarks': vod_remarks
};
});
return JSON.stringify({
list: videos
});
} catch (error) {
return JSON.stringify({
list: []
});
}
}
function getFilterValue(filterGroup, key, extend) {
const filterItem = filterGroup.find(item => item.key === key);
if (!filterItem) return '';
const userValue = extend[key];
if (userValue) {
const option = filterItem.value.find(opt => opt.v === userValue);
return option ? option.v : '';
}
return filterItem.value[0].v;
}
async function search(wd) {
const searchUrl = `${HOST}/nk/-------------.html?wd=${encodeURIComponent(wd)}`;
const lowerWd = wd.toLowerCase();
try {
const html = await requestWithCaptcha(searchUrl, 'search');
if (!html) return JSON.stringify({ list: [], error: '需要验证码但无法自动识别' });
const $ = load(html);
const hasSearchResults = $('.public-list-box.search-box').length > 0;
const hasSearchInput = $('input[name="wd"]').length > 0;
const hasSearchHeader = $('.m-search').length > 0;
if ((hasSearchInput || hasSearchHeader) && !hasSearchResults) {
return JSON.stringify({ list: [], error: '搜索无结果或需要验证码' });
}
if (!hasSearchInput && !hasSearchHeader && !hasSearchResults) {
return JSON.stringify({ list: [], error: '需要验证码但无法自动识别' });
}
const items = $('.public-list-box.search-box');
const videos = [];
items.each((index, item) => {
const $item = $(item);
const $link = $item.find('.public-list-exp');
const href = $link.attr('href');
if (!href) return;
const $title = $item.find('.thumb-txt a');
const vod_name = getText($title);
if (!vod_name) return;
const $img = $item.find('.gen-movie-img');
const vod_pic = $img.attr('data-src');
const $remarks = $item.find('.public-list-prb');
const vod_remarks = getText($remarks);
const $info = $item.find('.thumb-else');
const yearText = getText($info.find('a[href*="202"]').first());
const areaText = getText($info.find('a[href*="美国"], a[href*="英国"], a[href*="泰国"]').first());
const lowerTitle = vod_name.toLowerCase();
if (lowerTitle.includes(lowerWd)) {
videos.push({
'vod_id': HOST + href,
'vod_name': vod_name,
'vod_pic': vod_pic,
'vod_remarks': vod_remarks,
'vod_year': yearText,
'vod_area': areaText
});
}
});
return JSON.stringify({ list: videos });
} catch (error) {
return JSON.stringify({ list: [] });
}
}
async function detail(id) {
try {
const html = await request(id);
if (!html) return JSON.stringify({ list: [] });
const $ = load(html);
const vod = {
vod_id: id,
vod_name: getText($('.slide-info-title')),
vod_pic: $('.detail-pic img').attr('data-src') || $('.detail-pic img').attr('src'),
vod_year: '',
vod_area: '',
vod_remarks: '',
vod_actor: '',
vod_director: '',
vod_content: getText($('#height_limit')),
vod_play_from: '',
vod_play_url: ''
};
$('.slide-info').each((i, elem) => {
const $elem = $(elem);
const text = getText($elem);
if (text.includes('备注 :') || text.includes('备注 :')) {
vod.vod_remarks = text.replace(/备注\s*[::]/g, '').trim();
} else if (text.includes('导演 :') || text.includes('导演 :')) {
vod.vod_director = text.replace(/导演\s*[::]/g, '').replace(/[\/\s]+$/, '').trim();
} else if (text.includes('演员 :') || text.includes('演员 :')) {
vod.vod_actor = text.replace(/演员\s*[::]/g, '').replace(/[\/\s]+$/, '').trim();
} else if (text.includes('更新 :') || text.includes('更新 :')) {
const updateText = text.replace(/更新\s*[::]/g, '').trim();
const yearMatch = updateText.match(/\d{4}/);
if (yearMatch) {
vod.vod_year = yearMatch[0];
}
}
});
// 从链接中提取年份和地区
const yearLink = $('.slide-info a[href*="202"]').first();
if (yearLink.length) {
const yearText = getText(yearLink);
if (/^\d{4}$/.test(yearText)) vod.vod_year = yearText;
}
// 尝试从 slide-info-remarks 获取年份和地区
$('.slide-info-remarks a').each((i, elem) => {
const $a = $(elem);
const href = $a.attr('href') || '';
const aText = getText($a);
if (/\d{4}/.test(aText) && !vod.vod_year) {
vod.vod_year = aText.match(/\d{4}/)[0];
}
if ((href.includes('内地') || href.includes('香港') || href.includes('台湾') ||
href.includes('美国') || href.includes('韩国') || href.includes('日本') ||
href.includes('泰国') || href.includes('英国')) && !vod.vod_area) {
vod.vod_area = aText;
}
});
const playFrom = [];
const playUrl = [];
$('.anthology-tab .swiper-slide').each((i, tab) => {
const $tab = $(tab);
let from = $tab.attr('data-from') || '';
if (!from) from = `nk${i + 1}`;
// 获取线路名称,优先从文本中提取
let show = getText($tab);
// 清理文本:去除图标字符、badge数字等
show = show.replace(/[\ue000-\uf8ff]/g, '') // 去除图标字体
.replace(/&\w+;/g, '')
.replace(/\s*\d+\s*$/, '') // 去除末尾的badge数字
.trim();
if (!show) show = from;
playFrom.push(show);
});
$('.anthology-list-box').each((i, box) => {
const episodes = [];
const $tab = $('.anthology-tab .swiper-slide').eq(i);
let from = $tab.attr('data-from') || '';
if (!from) from = `nk${i + 1}`;
$(box).find('.anthology-list-play a').each((j, link) => {
const $link = $(link);
let episodeName = getText($link);
if (!episodeName) {
// 如果还是获取不到,从 href 中提取
const href = $link.attr('href') || '';
const match = href.match(/-(\d+)\.html$/);
episodeName = match ? `第${match[1]}集` : `第${j + 1}集`;
}
const episodeUrl = HOST + $link.attr('href');
episodes.push(`${episodeName}$${episodeUrl}@${from}`);
});
playUrl.push(episodes.join('#'));
});
vod.vod_play_from = playFrom.join('$$$');
vod.vod_play_url = playUrl.join('$$$');
return JSON.stringify({ list: [vod] });
} catch (error) {
return JSON.stringify({ list: [] });
}
}
async function play(flag, id, flags) {
try {
const parts = id.split('@');
if (parts.length < 2) {
throw new Error('播放链接格式错误,缺少英文标识');
}
const playUrl = parts[0];
const from = parts[1];
const html = await request(playUrl);
const playerStart = html.indexOf('var player_aaaa=');
if (playerStart === -1) throw new Error('未找到player_aaaa');
const playerEnd = html.indexOf('</script>', playerStart);
if (playerEnd === -1) throw new Error('未找到脚本结束标签');
let playerScript = html.substring(playerStart + 'var player_aaaa='.length, playerEnd);
playerScript = playerScript.split(';')[0].trim()
.replace(/,\s*$/, '')
.replace(/\/\*[\s\S]*?\*\//g, '')
.replace(/\/\/.*/g, '');
let playerData;
try {
playerData = JSON.parse(playerScript);
} catch (parseErr) {
eval(`playerData = ${playerScript}`);
}
if (!playerData || !playerData.url) {
throw new Error('播放数据格式异常: url 缺失');
}
let playurl = playerData.url;
if (/m3u8|mp4/i.test(playurl)) {
return JSON.stringify({ parse: 0, url: playurl });
}
const configUrl = `${HOST}/static/js/playerconfig.js?t=20251225`;
const configJs = await request(configUrl);
if (!configJs) throw new Error('获取 playerconfig.js 失败');
let MacPlayerConfig = {};
try {
const evalCode = `(function(){
var MacPlayerConfig = {};
${configJs.replace(/^var MacPlayerConfig=.*?;/, '')}
return MacPlayerConfig;
})()`;
MacPlayerConfig = eval(evalCode);
} catch (e) {
MacPlayerConfig = {
player_list: {
nk1: { parse: 'https://gg.xn--it-if7c19g5s4bps5c.com/nkvod3.php' },
nk2: { parse: 'https://gg.xn--it-if7c19g5s4bps5c.com/nkvod3.php' },
nk4k: { parse: 'https://gg.xn--it-if7c19g5s4bps5c.com/nkvod3.php' }
}
};
}
const playerList = MacPlayerConfig.player_list || {};
let parseDomain = playerList[from]?.parse || playerList.nk4k.parse;
let jx = '';
if (parseDomain.includes('?')) {
jx = `${parseDomain}&url=${encodeURIComponent(playurl)}&next=${encodeURIComponent(playerData.link_next || '')}&title=${encodeURIComponent(playerData.vod_data?.vod_name || '')}`;
} else {
jx = `${parseDomain}?url=${encodeURIComponent(playurl)}&next=${encodeURIComponent(playerData.link_next || '')}&title=${encodeURIComponent(playerData.vod_data?.vod_name || '')}`;
}
const jxhtml = await request(jx);
if (!jxhtml) throw new Error('解析接口响应为空');
let raw_key = [], iv = [], encrypted = '';
const rawKeyMatch = jxhtml.match(/var\s+raw_key\s*=\s*\[([^\]]+)\]/);
if (rawKeyMatch) {
raw_key = rawKeyMatch[1].split(',').map(item => parseInt(item.trim())).filter(num => !isNaN(num));
}
const ivMatch = jxhtml.match(/var\s+iv\s*=\s*\[([^\]]+)\]/);
if (ivMatch) {
iv = ivMatch[1].split(',').map(item => parseInt(item.trim())).filter(num => !isNaN(num));
}
const encryptedMatch = jxhtml.match(/var\s+encrypted\s*=\s*["']([a-fA-F0-9]+)["']/);
if (encryptedMatch) {
encrypted = encryptedMatch[1];
}
const validKeyLengths = [16, 32];
const validIVLength = 16;
if (!encrypted || !validKeyLengths.includes(raw_key.length) || iv.length !== validIVLength) {
throw new Error(`加密参数提取失败或长度异常 → key:${raw_key.length}, iv:${iv.length}, encrypted:${encrypted.length}`);
}
const decryptedText = decryptData(encrypted, raw_key, iv);
if (!decryptedText) {
throw new Error('解密失败');
}
let finalPlayUrl = '';
const randomMatch = decryptedText.match(/getrandom\(['"]([^'"]+)['"]\)/);
if (randomMatch) {
finalPlayUrl = getrandom(randomMatch[1]);
} else {
const urlPattern = /https?:\/\/[^\s"'<>]+/gi;
const urlMatches = decryptedText.match(urlPattern);
if (urlMatches && urlMatches.length > 0) {
finalPlayUrl = urlMatches[0];
} else {
throw new Error('未找到 getrandom 参数或URL');
}
}
return JSON.stringify({ parse: 0, url: finalPlayUrl });
} catch (error) {
return JSON.stringify({ parse: 1, url: id, error: error.message });
}
}
function validateHexData(hexStr) {
if (hexStr.length % 2 !== 0) {
return false;
}
if (!/^[0-9a-fA-F]+$/.test(hexStr)) {
return false;
}
return true;
}
function decryptData(encryptedHex, rawKeyArray, ivArray) {
if (!validateHexData(encryptedHex)) {
return null;
}
const rawKey = Crypto.lib.WordArray.create(new Uint8Array(rawKeyArray));
const iv = Crypto.lib.WordArray.create(new Uint8Array(ivArray));
const encrypted = Crypto.enc.Hex.parse(encryptedHex);
const modes = [
{ name: 'CBC', mode: Crypto.mode.CBC },
{ name: 'OFB', mode: Crypto.mode.OFB },
{ name: 'CTR', mode: Crypto.mode.CTR },
{ name: 'ECB', mode: Crypto.mode.ECB },
{ name: 'CFB', mode: Crypto.mode.CFB }
];
for (let modeInfo of modes) {
try {
const decryptionOptions = {
mode: modeInfo.mode,
padding: Crypto.pad.Pkcs7
};
if (modeInfo.name !== 'ECB') decryptionOptions.iv = iv;
const decrypted = Crypto.AES.decrypt({ ciphertext: encrypted }, rawKey, decryptionOptions);
const result = decrypted.toString(Crypto.enc.Utf8);
if (result && result.length > 0) {
return result;
}
} catch (e) {
}
}
return null;
}
function getrandom(b) {
try {
const string = b.substring(10);
let substr;
try {
const base64Data = Crypto.enc.Base64.parse(string);
substr = base64Data.toString(Crypto.enc.Latin1);
} catch (e) {
throw new Error('Base64解码失败');
}
if (!substr) {
throw new Error('Base64解码结果为空');
}
const substrTrim = substr.substring(10);
let data2 = substrTrim.replace('_nanke', '');
let data3 = data2.slice(0, 20) + data2.slice(21);
const hexStr = data3.replace(/[^0-9a-fA-F]/g, '');
const finalUrl = hexDecodeAndFilter(hexStr);
return finalUrl;
} catch (error) {
throw error;
}
}
function hexDecodeAndFilter(hexStr) {
try {
let pureHex = hexStr.replace(/[^0-9a-fA-F]/g, '');
if (pureHex.length % 2 !== 0) {
pureHex += '0';
}
let decoded = '';
for (let i = 0; i < pureHex.length; i += 2) {
const hexByte = pureHex.substr(i, 2);
const charCode = parseInt(hexByte, 16);
const char = String.fromCharCode(charCode);
const isUrlValid = /[a-zA-Z0-9:\/\.\-\?\&=\%]/.test(char);
if (isUrlValid) {
decoded += char;
}
}
const urlMatch = decoded.match(/https?:\/\/[^\s]+/);
decoded = urlMatch ? urlMatch[0] : decoded.trim();
return decoded;
} catch (e) {
throw e;
}
}
export function __jsEvalReturn() {
return {
init: init,
detail: detail,
home: home,
play: play,
search: search,
homeVod: homeVod,
category: category
};
}