Files
tvbox_collect/tianjitop/llllo4k/js/4KAV.js
T
2026-07-17 09:35:49 +00:00

333 lines
11 KiB
JavaScript

import { Crypto, load, _ } from 'assets://js/lib/cat.js';
let HOST = 'https://www.4k-av.com';
let siteKey = '';
let siteType = 0;
const COMMON_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
'Referer': HOST + '/'
};
// ----- 画质映射表(标准目录名) -----
const QUALITY_MAP = {
'4k': '4K',
'2160p': '4K',
'2k': '1080P',
'1080p': '1080P',
'1080': '1080P',
'720p': '720P',
'720': '720P'
};
// 将任意画质描述转换为标准目录名
function normalizeQuality(q) {
const lower = q.toLowerCase().replace(/\s+/g, '');
for (const [key, val] of Object.entries(QUALITY_MAP)) {
if (lower.includes(key)) return val;
}
return '1080P'; // 默认
}
// ----- 基础网络请求 -----
async function request(url) {
try {
const res = await req(url, {
method: 'get',
headers: COMMON_HEADERS,
timeout: 5000
});
return res.content;
} catch (e) {
return '';
}
}
// ----- 初始化 -----
function init(cfg) {
siteKey = cfg.skey;
siteType = cfg.stype;
if (cfg.ext) {
HOST = cfg.ext.replace(/\/$/, '');
}
}
// ----- 首页分类 -----
async function home(filter) {
const classes = [
{ type_id: 'tv', type_name: '电视剧' },
{ type_id: 'movie', type_name: '电影' }
];
// 提取的筛选配置
const filterConfig = [
{
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" }
]
},
{
key: "tag",
name: "标签",
value: [
{ n: "全部", v: "" },
{ n: "动作", v: "tag/动作" },
{ n: "剧情", v: "tag/剧情" },
{ n: "冒险", v: "tag/冒险" },
{ n: "喜剧", v: "tag/喜剧" },
{ n: "国产剧", v: "tag/国产剧" },
{ n: "恐怖", v: "tag/恐怖" },
{ n: "战争", v: "tag/战争" },
{ n: "科幻", v: "tag/科幻" },
{ n: "动画", v: "tag/动画" },
{ n: "韩剧", v: "tag/韩剧" },
{ n: "犯罪", v: "tag/犯罪" },
{ n: "纪录片", v: "tag/纪录片" }
]
}
];
const filters = {
"tv": filterConfig,
"movie": filterConfig
};
return JSON.stringify({ class: classes, filters: filters });
}
// ----- 通用列表解析(首页、分类、搜索) -----
function parseVodList(html) {
const $ = load(html);
const list = [];
$('.NTMitem, .RTMitem').each((_, el) => {
const item = $(el);
const a = item.find('.title a');
const href = a.attr('href');
const name = a.text().trim() || a.attr('title');
const poster = item.find('.poster img');
let pic = poster.attr('src') || poster.attr('data-original');
if (pic && pic.startsWith('/')) pic = HOST + pic;
let remarks = item.find('.resyear label[title="分辨率"]').text().trim();
if (!remarks) remarks = item.find('.poster span').text().trim();
if (href && name) {
list.push({
vod_id: href,
vod_name: name,
vod_pic: pic,
vod_remarks: remarks
});
}
});
return list;
}
// ----- 首页推荐 -----
async function homeVod() {
const html = await request(HOST + '/');
const list = parseVodList(html);
return JSON.stringify({ list });
}
// ----- 分类页 -----
async function category(tid, pg, filter, extend) {
let id = tid;
// 适配筛选参数,覆盖原本的栏目 id
if (extend && extend.year) {
id = extend.year;
} else if (extend && extend.tag) {
id = extend.tag;
}
let url = pg > 1 ? `${HOST}/${id}/page-${pg}.html` : `${HOST}/${id}/`;
const html = await request(url);
const list = parseVodList(html);
return JSON.stringify({
page: parseInt(pg),
pagecount: list.length === 0 ? parseInt(pg) : parseInt(pg) + 1,
limit: 20,
total: list.length === 0 ? parseInt(pg) * 20 : (parseInt(pg) + 1) * 20,
list
});
}
// ----- 详情页(支持多画质线路) -----
async function detail(id) {
let url = id.startsWith('http') ? id : HOST + id;
const html = await request(url);
const $ = load(html);
// 1. 提取分辨率列表
let resList = [];
$('#MainContent_videodetail label').each((_, el) => {
const text = $(el).text();
if (text.includes('分辨率')) {
const raw = text.replace('分辨率:', '').trim();
resList = raw.split('/').map(s => s.trim()).filter(Boolean);
}
});
if (resList.length === 0) resList = ['1080P']; // 兜底
// 2. 提取标签与演员
const tags = [];
$('#MainContent_tags a').each((_, el) => tags.push($(el).text().trim()));
let actor = '';
const subtitle = $('#MainContent_titleh12 h2').text().trim();
if (subtitle) {
const parts = subtitle.split('/');
if (parts.length > 1) actor = parts.slice(1).join('/').trim();
}
// 3. 构建基础剧集列表
const epList = [];
$('#rtlist li').each((_, el) => {
const item = $(el);
const a = item.find('a');
if (a.length) {
const epUrl = a.attr('href');
const epName = a.find('span').text().trim() || a.attr('title') || '播放';
epList.push({ name: epName, url: epUrl });
} else {
// 当前播放集(无链接)
const epName = item.find('span.stitle').text().trim() || '正片';
epList.push({ name: epName, url: id }); // 使用当前详情页ID
}
});
if (epList.length === 0) epList.push({ name: '正片', url: id });
// 4. 为每个画质生成线路
const playFroms = [];
const playUrls = [];
resList.forEach(res => {
const dirName = normalizeQuality(res); // 标准化目录名
const lineName = `4KAV-${res}`;
playFroms.push(lineName);
// 每个剧集附加画质目录
const lineEpStr = epList.map(ep => `${ep.name}$${ep.url}|${dirName}`).join('#');
playUrls.push(lineEpStr);
});
// 5. 组装vod信息
const vod = {
vod_id: id,
vod_name: $('#tophead h1').text().trim() || $('.videodetail').attr('title'),
vod_pic: $('#MainContent_poster img').attr('src'),
type_name: tags.join(','),
vod_year: $('#MainContent_videodetail a').text().trim(),
vod_area: '',
vod_remarks: resList.join('/'),
vod_actor: actor,
vod_director: '',
vod_content: $('#MainContent_videodesc p').map((i, el) => $(el).text().trim()).get().join('\n'),
vod_play_from: playFroms.join('$$$'),
vod_play_url: playUrls.join('$$$')
};
if (vod.vod_pic && vod.vod_pic.startsWith('/')) vod.vod_pic = HOST + vod.vod_pic;
return JSON.stringify({ list: [vod] });
}
// ----- 搜索 -----
async function search(wd, quick, pg) {
const url = `${HOST}/s?d=${encodeURIComponent(wd)}`;
const html = await request(url);
const list = parseVodList(html);
return JSON.stringify({ list });
}
// ----- 播放(支持画质切换) -----
async function play(flag, id, flags) {
// id 格式: realPageUrl|targetDir
const parts = id.split('|');
const realId = parts[0];
const targetDir = parts[1] || '1080P'; // 默认1080P
const url = realId.startsWith('http') ? realId : HOST + realId;
const html = await request(url);
if (!html) {
return JSON.stringify({ parse: 0, url: '', header: {} });
}
const $ = load(html);
let playUrl = '';
// 方式1:从 video source 提取
const sources = $('video source');
if (sources.length) {
// 优先选择与目标画质匹配的 source(若有多个)
for (let i = 0; i < sources.length; i++) {
const src = sources.eq(i).attr('src');
if (src) {
// 如果当前source的路径包含目标画质目录,直接使用
if (src.includes(`/${targetDir}/`)) {
playUrl = src;
break;
}
// 否则记录第一个作为备选
if (!playUrl) playUrl = src;
}
}
}
// 方式2:正则匹配(如果未找到)
if (!playUrl) {
const match = html.match(/(https?:\/\/[^\s"'<>]+?\.(?:m3u8|mp4)[^\s"'<>]*)/);
if (match) playUrl = match[1];
}
// 如果依旧没有,返回空
if (!playUrl) {
return JSON.stringify({ parse: 0, url: '', header: {} });
}
// 替换画质目录(如果目标目录与当前不同)
if (targetDir && !playUrl.includes(`/${targetDir}/`)) {
// 匹配常见画质目录(忽略大小写)
const dirPattern = /\/(4K|1080P|720P|1080|720|2160P|2K)\//i;
if (dirPattern.test(playUrl)) {
playUrl = playUrl.replace(dirPattern, `/${targetDir}/`);
} else {
// 如果无法匹配,尝试在文件名前插入目录(较少见,但作为备选)
// 例如: /tv/xxx/xxx.m3u8 -> /tv/xxx/1080P/xxx.m3u8
const lastSlash = playUrl.lastIndexOf('/');
if (lastSlash !== -1) {
const base = playUrl.substring(0, lastSlash);
const file = playUrl.substring(lastSlash + 1);
playUrl = `${base}/${targetDir}/${file}`;
}
}
}
return JSON.stringify({
parse: 0,
url: playUrl,
header: {
'User-Agent': COMMON_HEADERS['User-Agent'],
'Referer': HOST + '/'
}
});
}
// ----- 导出接口 -----
export function __jsEvalReturn() {
return {
init,
home,
homeVod,
category,
detail,
search,
play
};
}