上传文件至「js」
This commit is contained in:
+105
@@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
@header({
|
||||||
|
searchable: 2,
|
||||||
|
filterable: 1,
|
||||||
|
quickSearch: 0,
|
||||||
|
title: '可可影视[优]',
|
||||||
|
'类型': '影视',
|
||||||
|
lang: 'dr2'
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
var rule = {
|
||||||
|
title: '可可影视[优]',
|
||||||
|
host: 'https://www.keke5.app',
|
||||||
|
//host: 'https://www.kkys01.com',
|
||||||
|
url: '/show/fyclass-----2-fypage.html',
|
||||||
|
//url: '/show/fyclass-fyfilter-fypage.html',
|
||||||
|
searchUrl: '/search?t=/fVll50WsMh42iBP633zlA==&k=**&page=fypage',
|
||||||
|
searchable: 2,
|
||||||
|
quickSearch: 0,
|
||||||
|
filterable: 1,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'MOBILE_UA',
|
||||||
|
},
|
||||||
|
class_parse: '#nav-swiper&&.nav-swiper-slide;a&&Text;a&&href;/(\\w+).html',
|
||||||
|
cate_exclude: 'Netflix|今日更新|专题列表|排行榜',
|
||||||
|
tab_exclude:'可可影视提供',
|
||||||
|
tab_order: ['超清', '蓝光', '极速蓝光'],
|
||||||
|
tab_remove:['4K(高峰不卡)'],
|
||||||
|
play_parse: true,
|
||||||
|
lazy: $js.toString(() => {
|
||||||
|
input = {
|
||||||
|
parse: 1,
|
||||||
|
url: input,
|
||||||
|
js: 'document.querySelector("#my-video video").click()',
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
limit: 20,
|
||||||
|
推荐: '.section-box:eq(2)&&.module-box-inner&&.module-item;*;*;*;*',
|
||||||
|
double: false,
|
||||||
|
一级: '.module-box-inner&&.module-item;.v-item-title:eq(1)&&Text;img:last-of-type&&data-original;.v-item-bottom&&span&&Text;a&&href',
|
||||||
|
二级: {
|
||||||
|
title: '.detail-pic&&img&&alt;.detail-tags&&a&&Text',
|
||||||
|
img: '.detail-pic&&img&&data-original',
|
||||||
|
desc: '.detail-info-row-main:eq(-2)&&Text;.detail-tags&&a&&Text;.detail-tags&&a:eq(1)&&Text;.detail-info-row-main:eq(1)&&Text;.detail-info-row-main&&Text',
|
||||||
|
content: '.detail-desc&&Text',
|
||||||
|
tabs: '.source-item-label',
|
||||||
|
//tabs: 'body&&.source-item-label[id]',
|
||||||
|
lists: '.episode-list:eq(#id) a',
|
||||||
|
},
|
||||||
|
搜索: '.search-result-list&&a;.title:eq(1)&&Text;.search-result-item-pic&&img&&data-original;.search-result-item-header&&Text;a&&href;.desc&&Text',
|
||||||
|
图片替换:'https://www.keke5.app=>https://vres.zyxpedu.com',
|
||||||
|
预处理: $js.toString(() => {
|
||||||
|
function extractHashFromResponse(ruleHost) {
|
||||||
|
try {
|
||||||
|
const response = request(ruleHost);
|
||||||
|
const regex = /a0_0x2a54\s*=\s*\['([^']+)'/;
|
||||||
|
const match = response.match(regex);
|
||||||
|
return match ? match[1] : '';
|
||||||
|
} catch (error) {
|
||||||
|
console.error('请求失败:', error);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function sha1ToUint8ArrayLatin1(input) {
|
||||||
|
const hash = CryptoJS.SHA1(input);
|
||||||
|
const latin1String = hash.toString(CryptoJS.enc.Latin1);
|
||||||
|
const uint8Array = new Uint8Array(latin1String.length);
|
||||||
|
|
||||||
|
for (let i = 0; i < latin1String.length; i++) {
|
||||||
|
uint8Array[i] = latin1String.charCodeAt(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
return uint8Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
function run(c, n1) {
|
||||||
|
let i = 0;
|
||||||
|
while (i < 1000000) {
|
||||||
|
const input = c + i;
|
||||||
|
const hash = sha1ToUint8ArrayLatin1(input);
|
||||||
|
if (hash[n1] === 0xb0 && hash[n1 + 1] === 0x0b) {
|
||||||
|
const myck = 'cdndefend_js_cookie=' + c + i;
|
||||||
|
console.log('找到 myck:', myck);
|
||||||
|
rule.headers['cookie'] = myck;
|
||||||
|
setItem('mycookie', myck);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
console.log('未找到符合条件的 i');
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash = extractHashFromResponse(rule.host);
|
||||||
|
if (hash != '' && hash != getItem('myhash')) {
|
||||||
|
setItem('mycookie', '');
|
||||||
|
setItem('myhash', hash);
|
||||||
|
run(hash, parseInt('0x' + hash[0], 16));
|
||||||
|
}
|
||||||
|
if (getItem('mycookie')) {
|
||||||
|
rule.headers['cookie'] = getItem('mycookie');
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
}
|
||||||
+1119
@@ -0,0 +1,1119 @@
|
|||||||
|
// @name 咖啡直播
|
||||||
|
// @author modified by ChatGPT
|
||||||
|
// @description 咖啡直播体育赛事直播 + 录像回放
|
||||||
|
// @version 1.3.0
|
||||||
|
|
||||||
|
//const host = 'https://kafeizhibo.com';
|
||||||
|
const host = 'https://kafeizhibo.cc';
|
||||||
|
const headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Mobile Safari/537.36',
|
||||||
|
'Accept': 'application/json, text/plain, */*',
|
||||||
|
'Referer': host + '/pc/live',
|
||||||
|
'Origin': host
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== 工具函数 ==========
|
||||||
|
|
||||||
|
function fixUrl(path) {
|
||||||
|
if (!path) return '';
|
||||||
|
const value = String(path).trim();
|
||||||
|
if (!value) return '';
|
||||||
|
if (value.startsWith('http')) return value;
|
||||||
|
if (value.startsWith('//')) return 'https:' + value;
|
||||||
|
return host + (value.startsWith('/') ? '' : '/') + value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeText(value, fallback = '') {
|
||||||
|
if (value === undefined || value === null) return fallback;
|
||||||
|
return String(value).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeNumber(value, fallback = '') {
|
||||||
|
if (value === undefined || value === null || value === '') return fallback;
|
||||||
|
return String(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanName(name) {
|
||||||
|
return safeText(name, '播放').replace(/[$#]/g, ' ').trim() || '播放';
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanPlayUrl(url) {
|
||||||
|
return safeText(url).replace(/#/g, '%23');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildQuery(params = {}) {
|
||||||
|
const arr = [];
|
||||||
|
for (const [key, value] of Object.entries(params)) {
|
||||||
|
if (value === undefined || value === null || value === '') continue;
|
||||||
|
arr.push(`${encodeURIComponent(key)}=${encodeURIComponent(value)}`);
|
||||||
|
}
|
||||||
|
return arr.join('&');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getContent(res) {
|
||||||
|
if (!res) return '';
|
||||||
|
if (typeof res === 'string') return res;
|
||||||
|
if (typeof res.content === 'string') return res.content;
|
||||||
|
if (typeof res.body === 'string') return res.body;
|
||||||
|
if (typeof res.data === 'string') return res.data;
|
||||||
|
if (typeof res.content === 'object') return JSON.stringify(res.content);
|
||||||
|
if (typeof res.body === 'object') return JSON.stringify(res.body);
|
||||||
|
if (typeof res.data === 'object') return JSON.stringify(res.data);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestText(url, params = {}, extraHeaders = {}) {
|
||||||
|
const query = buildQuery(params);
|
||||||
|
const finalUrl = query ? `${url}${url.includes('?') ? '&' : '?'}${query}` : url;
|
||||||
|
const r = await req(finalUrl, {
|
||||||
|
headers: Object.assign({}, headers, extraHeaders)
|
||||||
|
});
|
||||||
|
return getContent(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestJson(url, params = {}, extraHeaders = {}) {
|
||||||
|
const text = await requestText(url, params, extraHeaders);
|
||||||
|
if (!text) return null;
|
||||||
|
return JSON.parse(text);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestJsonSafe(url, params = {}, extraHeaders = {}) {
|
||||||
|
try {
|
||||||
|
return await requestJson(url, params, extraHeaders);
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function requestTextSafe(url, params = {}, extraHeaders = {}) {
|
||||||
|
try {
|
||||||
|
return await requestText(url, params, extraHeaders);
|
||||||
|
} catch (e) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isOk(data) {
|
||||||
|
return data && (
|
||||||
|
data.code === 200 ||
|
||||||
|
data.code === '200' ||
|
||||||
|
data.status === 200 ||
|
||||||
|
data.status === '200' ||
|
||||||
|
data.success === true ||
|
||||||
|
data.data !== undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function pick(obj, keys = [], fallback = '') {
|
||||||
|
if (!obj || typeof obj !== 'object') return fallback;
|
||||||
|
for (const key of keys) {
|
||||||
|
if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '') {
|
||||||
|
return obj[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
function todayString() {
|
||||||
|
const d = new Date();
|
||||||
|
const y = d.getFullYear();
|
||||||
|
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||||
|
const day = String(d.getDate()).padStart(2, '0');
|
||||||
|
return `${y}-${m}-${day}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function uniqueByVodId(list = []) {
|
||||||
|
const seen = new Set();
|
||||||
|
const result = [];
|
||||||
|
list.forEach(item => {
|
||||||
|
if (!item || !item.vod_id) return;
|
||||||
|
const key = String(item.vod_id);
|
||||||
|
if (seen.has(key)) return;
|
||||||
|
seen.add(key);
|
||||||
|
result.push(item);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function encodeLiveId(item = {}) {
|
||||||
|
try {
|
||||||
|
return 'live|' + encodeURIComponent(JSON.stringify(item));
|
||||||
|
} catch (e) {
|
||||||
|
return 'live|' + encodeURIComponent(JSON.stringify({
|
||||||
|
id: pick(item, ['id', 'match_id', 'matchId', 'room_id', 'roomId'])
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function decodeLiveId(id) {
|
||||||
|
const raw = String(id || '');
|
||||||
|
if (!raw.startsWith('live|')) return null;
|
||||||
|
try {
|
||||||
|
return JSON.parse(decodeURIComponent(raw.slice(5)));
|
||||||
|
} catch (e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function isMediaUrl(url) {
|
||||||
|
const value = safeText(url).toLowerCase();
|
||||||
|
if (!value) return false;
|
||||||
|
return value.includes('.m3u8') ||
|
||||||
|
value.includes('m3u8?') ||
|
||||||
|
value.includes('.flv') ||
|
||||||
|
value.includes('flv?') ||
|
||||||
|
value.includes('.mp4') ||
|
||||||
|
value.includes('mp4?') ||
|
||||||
|
value.startsWith('rtmp') ||
|
||||||
|
value.startsWith('rtsp') ||
|
||||||
|
value.startsWith('p2p') ||
|
||||||
|
value.startsWith('mitv');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLivePageUrl(url) {
|
||||||
|
const value = safeText(url).toLowerCase();
|
||||||
|
return value.includes('/pc/room/') || value.includes('/room/');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeTypeValue(tid) {
|
||||||
|
const raw = String(tid || '');
|
||||||
|
if (raw === 'live') return '';
|
||||||
|
if (raw === 'live_hot') return '';
|
||||||
|
if (raw === 'live_football') return '1';
|
||||||
|
if (raw === 'live_basketball') return '2';
|
||||||
|
if (raw === 'live_tennis') return '3';
|
||||||
|
if (raw === 'live_billiards') return '4';
|
||||||
|
if (raw === 'live_nba') return '';
|
||||||
|
if (raw === 'all') return '';
|
||||||
|
if (raw === 'football') return '1';
|
||||||
|
if (raw === 'basketball') return '2';
|
||||||
|
if (raw === 'nba') return '';
|
||||||
|
return raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinTitle(home, away, league) {
|
||||||
|
const h = safeText(home, '');
|
||||||
|
const a = safeText(away, '');
|
||||||
|
const l = safeText(league, '赛事');
|
||||||
|
|
||||||
|
if (h && a) return `${l} ${h} vs ${a}`;
|
||||||
|
if (h) return `${l} ${h}`;
|
||||||
|
if (a) return `${l} ${a}`;
|
||||||
|
return l;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 分类配置 ==========
|
||||||
|
|
||||||
|
const classes = [
|
||||||
|
{ type_id: 'live', type_name: '全部直播' },
|
||||||
|
{ type_id: 'live_hot', type_name: '热门直播' },
|
||||||
|
{ type_id: 'live_football', type_name: '足球直播' },
|
||||||
|
{ type_id: 'live_basketball', type_name: '篮球直播' },
|
||||||
|
{ type_id: 'live_nba', type_name: 'NBA直播' },
|
||||||
|
{ type_id: 'live_tennis', type_name: '网球直播' },
|
||||||
|
{ type_id: 'live_billiards', type_name: '台球直播' },
|
||||||
|
{ type_id: 'all', type_name: '全部录像' },
|
||||||
|
{ type_id: 'football', type_name: '足球录像' },
|
||||||
|
{ type_id: 'basketball', type_name: '篮球录像' },
|
||||||
|
{ type_id: 'nba', type_name: 'NBA录像' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const leagueAll = [
|
||||||
|
{ n: '全部', v: '' },
|
||||||
|
{ n: 'NBA', v: 'NBA' },
|
||||||
|
{ n: 'CBA', v: 'CBA' },
|
||||||
|
{ n: '英超', v: '英超' },
|
||||||
|
{ n: '西甲', v: '西甲' },
|
||||||
|
{ n: '意甲', v: '意甲' },
|
||||||
|
{ n: '德甲', v: '德甲' },
|
||||||
|
{ n: '法甲', v: '法甲' },
|
||||||
|
{ n: '欧冠', v: '欧冠' },
|
||||||
|
{ n: '中超', v: '中超' },
|
||||||
|
{ n: '中乙', v: '中乙' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const filters = {
|
||||||
|
live: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
live_hot: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
live_football: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
live_basketball: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
live_nba: [{ key: 'league', name: '联赛', value: [{ n: 'NBA', v: 'NBA' }] }],
|
||||||
|
all: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
football: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
basketball: [{ key: 'league', name: '联赛', value: leagueAll }],
|
||||||
|
nba: [{ key: 'league', name: '联赛', value: [{ n: 'NBA', v: 'NBA' }] }]
|
||||||
|
};
|
||||||
|
|
||||||
|
// ========== 通用数据提取 ==========
|
||||||
|
|
||||||
|
function findBestArray(node, depth = 0) {
|
||||||
|
if (!node || depth > 6) return [];
|
||||||
|
if (Array.isArray(node)) return node;
|
||||||
|
if (typeof node !== 'object') return [];
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
'list', 'items', 'rows', 'data', 'records',
|
||||||
|
'matches', 'matchList', 'schedules', 'scheduleList',
|
||||||
|
'lives', 'liveList', 'rooms', 'roomList',
|
||||||
|
'events', 'result'
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const key of keys) {
|
||||||
|
if (Array.isArray(node[key])) return node[key];
|
||||||
|
const arr = findBestArray(node[key], depth + 1);
|
||||||
|
if (arr.length) return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const value of Object.values(node)) {
|
||||||
|
const arr = findBestArray(value, depth + 1);
|
||||||
|
if (arr.length) return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getDataArray(data) {
|
||||||
|
if (!data) return [];
|
||||||
|
if (Array.isArray(data)) return data;
|
||||||
|
if (isOk(data)) return findBestArray(data.data);
|
||||||
|
return findBestArray(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLeagueText(item) {
|
||||||
|
return safeText(pick(item, [
|
||||||
|
'league_name', 'leagueName', 'league',
|
||||||
|
'competition_name', 'competitionName',
|
||||||
|
'event_name', 'eventName',
|
||||||
|
'match_type', 'matchType'
|
||||||
|
]), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSportTypeText(item) {
|
||||||
|
return safeText(pick(item, [
|
||||||
|
'type', 'type_id', 'typeId',
|
||||||
|
'sport_type', 'sportType',
|
||||||
|
'category_id', 'categoryId',
|
||||||
|
'sport', 'category'
|
||||||
|
]), '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function liveTitleText(item = {}) {
|
||||||
|
const title = safeText(pick(item, ['title', 'name', 'match_name', 'matchName'], ''));
|
||||||
|
const league = getLeagueText(item);
|
||||||
|
const home = safeText(pick(item, ['home_team', 'homeTeam', 'home_name', 'homeName', 'team1', 'home'], ''));
|
||||||
|
const away = safeText(pick(item, ['away_team', 'awayTeam', 'away_name', 'awayName', 'team2', 'away'], ''));
|
||||||
|
return [title, league, home, away].filter(Boolean).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function isLiveItem(item) {
|
||||||
|
if (!item || typeof item !== 'object') return false;
|
||||||
|
|
||||||
|
const status = safeText(pick(item, [
|
||||||
|
'status', 'match_status', 'matchStatus',
|
||||||
|
'state', 'live_status', 'liveStatus',
|
||||||
|
'status_text', 'statusText'
|
||||||
|
])).toLowerCase();
|
||||||
|
|
||||||
|
const liveFlag = pick(item, ['is_live', 'isLive', 'live', 'living'], '');
|
||||||
|
const hasLiveFlag = liveFlag === true || String(liveFlag) === '1';
|
||||||
|
|
||||||
|
const hasRoom = !!pick(item, [
|
||||||
|
'room_id', 'roomId', 'roomid',
|
||||||
|
'live_room_id', 'liveRoomId',
|
||||||
|
'room_url', 'roomUrl'
|
||||||
|
], '');
|
||||||
|
|
||||||
|
const hasPlayUrl = !!firstPlayableUrl(item);
|
||||||
|
|
||||||
|
if (hasLiveFlag || hasPlayUrl || hasRoom) return true;
|
||||||
|
if (status.includes('live')) return true;
|
||||||
|
if (status.includes('正在')) return true;
|
||||||
|
if (status.includes('直播')) return true;
|
||||||
|
if (status === '1' || status === '2') return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterLiveItems(items = [], league = '', type = '', isHot = false) {
|
||||||
|
const leagueText = safeText(league).toLowerCase();
|
||||||
|
const typeText = safeText(type);
|
||||||
|
|
||||||
|
return items.filter(item => {
|
||||||
|
if (!item || typeof item !== 'object') return false;
|
||||||
|
if (!isLiveItem(item)) return false;
|
||||||
|
|
||||||
|
const title = liveTitleText(item);
|
||||||
|
const titleLower = title.toLowerCase();
|
||||||
|
const itemLeague = getLeagueText(item).toLowerCase();
|
||||||
|
const itemType = getSportTypeText(item);
|
||||||
|
|
||||||
|
if (leagueText) {
|
||||||
|
if (!itemLeague.includes(leagueText) && !titleLower.includes(leagueText)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isHot) {
|
||||||
|
const hotValue = pick(item, ['is_hot', 'isHot', 'hot', 'recommend', 'is_recommend'], '');
|
||||||
|
const hotOk = hotValue === true || String(hotValue) === '1' || title.includes('热门');
|
||||||
|
if (!hotOk) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeText === '1') {
|
||||||
|
const ok = itemType === '1' ||
|
||||||
|
itemType.includes('足球') ||
|
||||||
|
title.includes('足球') ||
|
||||||
|
title.includes('英超') ||
|
||||||
|
title.includes('西甲') ||
|
||||||
|
title.includes('意甲') ||
|
||||||
|
title.includes('德甲') ||
|
||||||
|
title.includes('法甲') ||
|
||||||
|
title.includes('欧冠') ||
|
||||||
|
title.includes('中超') ||
|
||||||
|
title.includes('中乙');
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeText === '2') {
|
||||||
|
const ok = itemType === '2' ||
|
||||||
|
itemType.includes('篮球') ||
|
||||||
|
title.includes('篮球') ||
|
||||||
|
title.includes('NBA') ||
|
||||||
|
title.includes('CBA') ||
|
||||||
|
title.includes('WNBA');
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeText === '3') {
|
||||||
|
const ok = itemType === '3' || itemType.includes('网球') || title.includes('网球');
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeText === '4') {
|
||||||
|
const ok = itemType === '4' || itemType.includes('台球') || title.includes('台球');
|
||||||
|
if (!ok) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 直播接口 ==========
|
||||||
|
|
||||||
|
async function fetchLiveByApi(page = 1, size = 30, league = '', type = '', isHot = false) {
|
||||||
|
const date = todayString();
|
||||||
|
|
||||||
|
const urls = [
|
||||||
|
`${host}/api/v1/live`,
|
||||||
|
`${host}/api/v1/lives`,
|
||||||
|
`${host}/api/v1/live/list`,
|
||||||
|
`${host}/api/v1/lives/list`,
|
||||||
|
`${host}/api/v1/live/matches`,
|
||||||
|
`${host}/api/v1/matches/live`,
|
||||||
|
`${host}/api/v1/rooms`,
|
||||||
|
`${host}/api/v1/room/list`,
|
||||||
|
`${host}/api/v1/schedule`,
|
||||||
|
`${host}/api/v1/schedules`,
|
||||||
|
`${host}/api/v1/schedule/today`,
|
||||||
|
`${host}/api/v1/matches`
|
||||||
|
];
|
||||||
|
|
||||||
|
const paramList = [
|
||||||
|
{ page, size, limit: size, league, type, date },
|
||||||
|
{ page, pageSize: size, league, type, date },
|
||||||
|
{ page, per_page: size, league, type, date },
|
||||||
|
{ page, size, league, type, status: 'live' },
|
||||||
|
{ page, size, league, type, live: 1 },
|
||||||
|
{ page, size, league, type, is_live: 1 }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const url of urls) {
|
||||||
|
for (const params of paramList) {
|
||||||
|
const cleanParams = {};
|
||||||
|
Object.keys(params).forEach(key => {
|
||||||
|
if (params[key] !== undefined && params[key] !== null && params[key] !== '') {
|
||||||
|
cleanParams[key] = params[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await requestJsonSafe(url, cleanParams, {
|
||||||
|
Referer: host + '/pc/live'
|
||||||
|
});
|
||||||
|
|
||||||
|
const arr = getDataArray(data);
|
||||||
|
if (!arr.length) continue;
|
||||||
|
|
||||||
|
const filtered = filterLiveItems(arr, league, type, isHot);
|
||||||
|
if (filtered.length) return filtered.slice(0, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseLiveCardsFromHtml(html = '') {
|
||||||
|
const text = safeText(html);
|
||||||
|
if (!text || text === '加载中...') return [];
|
||||||
|
|
||||||
|
const list = [];
|
||||||
|
const roomReg = /\/pc\/room\/(\d+)/g;
|
||||||
|
let m;
|
||||||
|
|
||||||
|
while ((m = roomReg.exec(text)) !== null) {
|
||||||
|
const roomId = m[1];
|
||||||
|
const start = Math.max(0, m.index - 800);
|
||||||
|
const end = Math.min(text.length, m.index + 800);
|
||||||
|
const block = text.slice(start, end);
|
||||||
|
|
||||||
|
const titleMatch = block.match(/([\u4e00-\u9fa5A-Za-z0-9\s]+?\s+vs\s+[\u4e00-\u9fa5A-Za-z0-9\s]+)/i);
|
||||||
|
const title = titleMatch ? titleMatch[1].replace(/\s+/g, ' ').trim() : `直播房间 ${roomId}`;
|
||||||
|
|
||||||
|
const imgMatch = block.match(/https?:\/\/[^"'<>]+?\.(?:jpg|jpeg|png|webp)/i);
|
||||||
|
const pic = imgMatch ? imgMatch[0] : '';
|
||||||
|
|
||||||
|
list.push({
|
||||||
|
id: roomId,
|
||||||
|
room_id: roomId,
|
||||||
|
room_url: `${host}/pc/room/${roomId}`,
|
||||||
|
title: title,
|
||||||
|
cover: pic,
|
||||||
|
status_text: 'LIVE',
|
||||||
|
is_live: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLiveByPage(page = 1, size = 30, league = '', type = '', isHot = false) {
|
||||||
|
const html = await requestTextSafe(`${host}/pc/live`, {}, {
|
||||||
|
Referer: host + '/pc/live',
|
||||||
|
Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
|
||||||
|
});
|
||||||
|
|
||||||
|
const arr = parseLiveCardsFromHtml(html);
|
||||||
|
return filterLiveItems(arr, league, type, isHot).slice(0, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLiveMatches(page = 1, size = 30, league = '', type = '', isHot = false) {
|
||||||
|
let list = await fetchLiveByApi(page, size, league, type, isHot);
|
||||||
|
|
||||||
|
if (!list.length) {
|
||||||
|
list = await fetchLiveByPage(page, size, league, type, isHot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchLiveDetail(roomOrMatchId) {
|
||||||
|
const id = safeText(roomOrMatchId);
|
||||||
|
if (!id) return null;
|
||||||
|
|
||||||
|
const urls = [
|
||||||
|
`${host}/api/v1/room/${encodeURIComponent(id)}`,
|
||||||
|
`${host}/api/v1/rooms/${encodeURIComponent(id)}`,
|
||||||
|
`${host}/api/v1/live/${encodeURIComponent(id)}`,
|
||||||
|
`${host}/api/v1/lives/${encodeURIComponent(id)}`,
|
||||||
|
`${host}/api/v1/match/${encodeURIComponent(id)}/live`,
|
||||||
|
`${host}/api/v1/match/${encodeURIComponent(id)}/streams`,
|
||||||
|
`${host}/api/v1/matches/${encodeURIComponent(id)}`,
|
||||||
|
`${host}/api/v1/match/${encodeURIComponent(id)}`
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const url of urls) {
|
||||||
|
const data = await requestJsonSafe(url, {}, {
|
||||||
|
Referer: `${host}/pc/room/${encodeURIComponent(id)}`
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!data) continue;
|
||||||
|
if (isOk(data)) return data.data;
|
||||||
|
if (typeof data === 'object') return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 录像接口 ==========
|
||||||
|
|
||||||
|
async function fetchRecordings(page = 1, size = 30, league = '', type = '') {
|
||||||
|
const params = { page, size };
|
||||||
|
|
||||||
|
if (league) {
|
||||||
|
params.league = league;
|
||||||
|
} else if (type && type !== 'all' && type !== 'nba') {
|
||||||
|
params.type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await requestJsonSafe(`${host}/api/v1/recordings`, params, {
|
||||||
|
Referer: host + '/pc/replay'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchMatchRecordings(matchId) {
|
||||||
|
return await requestJsonSafe(`${host}/api/v1/match/${encodeURIComponent(matchId)}/recordings`, {}, {
|
||||||
|
Referer: host + '/pc/replay'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 播放源提取 ==========
|
||||||
|
|
||||||
|
function addPlayable(arr, name, url) {
|
||||||
|
const value = cleanPlayUrl(url);
|
||||||
|
if (!value) return;
|
||||||
|
const line = `${cleanName(name)}$${value}`;
|
||||||
|
if (arr.indexOf(line) >= 0) return;
|
||||||
|
arr.push(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectPlayableLinks(node, prefix = '线路', arr = [], depth = 0) {
|
||||||
|
if (!node || depth > 7) return arr;
|
||||||
|
|
||||||
|
if (typeof node === 'string') {
|
||||||
|
if (isMediaUrl(node)) {
|
||||||
|
addPlayable(arr, prefix, node);
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(node)) {
|
||||||
|
node.forEach((item, idx) => {
|
||||||
|
collectPlayableLinks(item, `${prefix}${idx + 1}`, arr, depth + 1);
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof node !== 'object') return arr;
|
||||||
|
|
||||||
|
const name = cleanName(pick(node, [
|
||||||
|
'name', 'title', 'line', 'line_name', 'lineName',
|
||||||
|
'source_name', 'sourceName', 'quality', 'label'
|
||||||
|
], prefix));
|
||||||
|
|
||||||
|
const directKeys = [
|
||||||
|
'play_url', 'playUrl',
|
||||||
|
'video_url', 'videoUrl',
|
||||||
|
'stream_url', 'streamUrl',
|
||||||
|
'live_url', 'liveUrl',
|
||||||
|
'm3u8', 'flv', 'hls',
|
||||||
|
'url', 'src', 'link',
|
||||||
|
'hd', 'sd', 'uhd',
|
||||||
|
'source', 'stream'
|
||||||
|
];
|
||||||
|
|
||||||
|
directKeys.forEach(key => {
|
||||||
|
const value = node[key];
|
||||||
|
if (typeof value === 'string' && isMediaUrl(value)) {
|
||||||
|
addPlayable(arr, name, value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Object.keys(node).forEach(key => {
|
||||||
|
if ([
|
||||||
|
'cover', 'cover_image', 'coverImage',
|
||||||
|
'logo', 'home_team_logo', 'away_team_logo',
|
||||||
|
'homeLogo', 'awayLogo', 'avatar'
|
||||||
|
].includes(key)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
collectPlayableLinks(node[key], name, arr, depth + 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function firstPlayableUrl(item) {
|
||||||
|
const arr = collectPlayableLinks(item, '线路', [], 0);
|
||||||
|
if (!arr.length) return '';
|
||||||
|
const parts = arr[0].split('$');
|
||||||
|
return parts.length > 1 ? parts.slice(1).join('$') : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildLivePlayLine(liveItem = {}, detailData = null) {
|
||||||
|
const arr = [];
|
||||||
|
|
||||||
|
collectPlayableLinks(liveItem, '线路', arr);
|
||||||
|
collectPlayableLinks(detailData, '线路', arr);
|
||||||
|
|
||||||
|
if (!arr.length) {
|
||||||
|
const roomId = safeText(pick(liveItem, [
|
||||||
|
'_live_room_id',
|
||||||
|
'room_id', 'roomId', 'roomid',
|
||||||
|
'live_room_id', 'liveRoomId',
|
||||||
|
'id', 'match_id', 'matchId'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const pageUrl = safeText(pick(liveItem, [
|
||||||
|
'_live_page_url',
|
||||||
|
'room_url', 'roomUrl',
|
||||||
|
'page_url', 'pageUrl',
|
||||||
|
'link'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const finalPage = pageUrl || (roomId ? `${host}/pc/room/${encodeURIComponent(roomId)}` : `${host}/pc/live`);
|
||||||
|
addPlayable(arr, '打开直播间', fixUrl(finalPage));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr.join('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildEpisodeLine(items = [], prefix = '录像') {
|
||||||
|
const arr = [];
|
||||||
|
if (!Array.isArray(items)) return '';
|
||||||
|
|
||||||
|
items.forEach((rec, idx) => {
|
||||||
|
const url = cleanPlayUrl(pick(rec, [
|
||||||
|
'video_url', 'videoUrl',
|
||||||
|
'url',
|
||||||
|
'play_url', 'playUrl'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
if (!url) return;
|
||||||
|
|
||||||
|
const name = cleanName(pick(rec, ['title', 'name'], `${prefix}${idx + 1}`));
|
||||||
|
arr.push(`${name}$${url}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
return arr.join('#');
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 列表解析 ==========
|
||||||
|
|
||||||
|
function parseLiveList(items = []) {
|
||||||
|
if (!Array.isArray(items)) return [];
|
||||||
|
|
||||||
|
return items.map(item => {
|
||||||
|
const titleRaw = safeText(pick(item, ['title', 'name', 'match_name', 'matchName'], ''));
|
||||||
|
const league = safeText(getLeagueText(item), '直播');
|
||||||
|
|
||||||
|
const home = safeText(pick(item, [
|
||||||
|
'home_team', 'homeTeam',
|
||||||
|
'home_name', 'homeName',
|
||||||
|
'team1', 'home'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const away = safeText(pick(item, [
|
||||||
|
'away_team', 'awayTeam',
|
||||||
|
'away_name', 'awayName',
|
||||||
|
'team2', 'away'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const title = titleRaw || joinTitle(home, away, league);
|
||||||
|
|
||||||
|
const time = safeText(pick(item, [
|
||||||
|
'start_time', 'startTime',
|
||||||
|
'match_time', 'matchTime',
|
||||||
|
'time'
|
||||||
|
], '正在直播'));
|
||||||
|
|
||||||
|
const roomId = safeText(pick(item, [
|
||||||
|
'room_id', 'roomId', 'roomid',
|
||||||
|
'live_room_id', 'liveRoomId',
|
||||||
|
'id', 'match_id', 'matchId'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const status = safeText(pick(item, [
|
||||||
|
'status_text', 'statusText',
|
||||||
|
'status', 'match_status', 'matchStatus',
|
||||||
|
'state'
|
||||||
|
], 'LIVE'));
|
||||||
|
|
||||||
|
let pic = pick(item, [
|
||||||
|
'cover_image', 'coverImage',
|
||||||
|
'cover', 'thumb', 'thumbnail',
|
||||||
|
'home_team_logo', 'homeLogo',
|
||||||
|
'away_team_logo', 'awayLogo',
|
||||||
|
'logo', 'pic'
|
||||||
|
], '');
|
||||||
|
|
||||||
|
pic = fixUrl(pic);
|
||||||
|
|
||||||
|
const liveItem = Object.assign({}, item, {
|
||||||
|
_live_title: title,
|
||||||
|
_live_league: league,
|
||||||
|
_live_time: time,
|
||||||
|
_live_room_id: roomId,
|
||||||
|
_live_page_url: roomId ? `${host}/pc/room/${encodeURIComponent(roomId)}` : fixUrl(pick(item, ['room_url', 'roomUrl', 'link'], ''))
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
vod_id: encodeLiveId(liveItem),
|
||||||
|
vod_name: title,
|
||||||
|
vod_pic: pic,
|
||||||
|
vod_remarks: `🔴 ${status || 'LIVE'} | ${time}`
|
||||||
|
};
|
||||||
|
}).filter(v => v.vod_id && v.vod_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseVideoList(items = [], showScore = true) {
|
||||||
|
if (!Array.isArray(items)) return [];
|
||||||
|
|
||||||
|
return items.map(item => {
|
||||||
|
const home = safeText(pick(item, [
|
||||||
|
'home_team', 'homeTeam',
|
||||||
|
'home_name', 'homeName',
|
||||||
|
'team1', 'home'
|
||||||
|
], '主队'));
|
||||||
|
|
||||||
|
const away = safeText(pick(item, [
|
||||||
|
'away_team', 'awayTeam',
|
||||||
|
'away_name', 'awayName',
|
||||||
|
'team2', 'away'
|
||||||
|
], '客队'));
|
||||||
|
|
||||||
|
const league = safeText(pick(item, [
|
||||||
|
'league_name', 'leagueName',
|
||||||
|
'league',
|
||||||
|
'competition_name', 'competitionName'
|
||||||
|
], '赛事'));
|
||||||
|
|
||||||
|
const title = joinTitle(home, away, league);
|
||||||
|
|
||||||
|
const homeScore = safeNumber(pick(item, ['home_score', 'homeScore', 'score_home'], '-'));
|
||||||
|
const awayScore = safeNumber(pick(item, ['away_score', 'awayScore', 'score_away'], '-'));
|
||||||
|
const score = `${homeScore} - ${awayScore}`;
|
||||||
|
|
||||||
|
const time = safeText(pick(item, [
|
||||||
|
'start_time', 'startTime',
|
||||||
|
'match_time', 'matchTime',
|
||||||
|
'time'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const count = safeNumber(pick(item, [
|
||||||
|
'recording_count', 'recordingCount',
|
||||||
|
'replay_count', 'video_count'
|
||||||
|
], '0'));
|
||||||
|
|
||||||
|
let pic = pick(item, [
|
||||||
|
'cover_image', 'coverImage',
|
||||||
|
'cover',
|
||||||
|
'home_team_logo', 'homeLogo',
|
||||||
|
'away_team_logo', 'awayLogo',
|
||||||
|
'logo', 'pic'
|
||||||
|
], '');
|
||||||
|
|
||||||
|
pic = fixUrl(pic);
|
||||||
|
|
||||||
|
if (pic.includes('default_cover')) {
|
||||||
|
pic = fixUrl(pick(item, [
|
||||||
|
'home_team_logo', 'homeLogo',
|
||||||
|
'away_team_logo', 'awayLogo',
|
||||||
|
'logo'
|
||||||
|
], ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
vod_id: String(pick(item, ['match_id', 'matchId', 'id'], '')),
|
||||||
|
vod_name: title,
|
||||||
|
vod_pic: pic,
|
||||||
|
vod_remarks: showScore ? `${score} | ${time} | ${count}个录像` : time
|
||||||
|
};
|
||||||
|
}).filter(v => v.vod_id && v.vod_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ========== 壳子接口 ==========
|
||||||
|
|
||||||
|
async function init(cfg) {
|
||||||
|
return JSON.stringify({});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function home(filter) {
|
||||||
|
try {
|
||||||
|
return JSON.stringify({
|
||||||
|
class: classes,
|
||||||
|
filters: filters
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({ class: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function homeVod() {
|
||||||
|
try {
|
||||||
|
const liveItems = await fetchLiveMatches(1, 30, '', '', false);
|
||||||
|
const liveList = parseLiveList(liveItems);
|
||||||
|
|
||||||
|
const data = await fetchRecordings(1, 20);
|
||||||
|
const replayList = isOk(data) ? parseVideoList(getDataArray(data)) : [];
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
list: uniqueByVodId([].concat(liveList, replayList))
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({ list: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function category(tid, pg, filter, extend = {}) {
|
||||||
|
try {
|
||||||
|
const page = parseInt(pg) || 1;
|
||||||
|
const size = 30;
|
||||||
|
let league = '';
|
||||||
|
let type = normalizeTypeValue(tid);
|
||||||
|
const isHot = String(tid || '') === 'live_hot';
|
||||||
|
|
||||||
|
if (extend && extend.league) {
|
||||||
|
league = extend.league;
|
||||||
|
} else if (String(tid || '') === 'live_nba' || String(tid || '') === 'nba') {
|
||||||
|
league = 'NBA';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String(tid || '').startsWith('live')) {
|
||||||
|
const liveItems = await fetchLiveMatches(page, size, league, type, isHot);
|
||||||
|
const list = parseLiveList(liveItems);
|
||||||
|
const pagecount = list.length >= size ? page + 1 : page;
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
page: page,
|
||||||
|
pagecount: pagecount,
|
||||||
|
limit: size,
|
||||||
|
total: list.length,
|
||||||
|
list: list
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await fetchRecordings(page, size, league, type);
|
||||||
|
const list = isOk(data) ? parseVideoList(getDataArray(data)) : [];
|
||||||
|
const pagecount = list.length >= size ? page + 1 : page;
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
page: page,
|
||||||
|
pagecount: pagecount,
|
||||||
|
limit: size,
|
||||||
|
total: list.length,
|
||||||
|
list: list
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
page: parseInt(pg) || 1,
|
||||||
|
pagecount: 0,
|
||||||
|
limit: 0,
|
||||||
|
total: 0,
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detail(id) {
|
||||||
|
try {
|
||||||
|
const rawId = String(id || '').trim();
|
||||||
|
if (!rawId) return JSON.stringify({ list: [] });
|
||||||
|
|
||||||
|
const liveItem = decodeLiveId(rawId);
|
||||||
|
|
||||||
|
if (liveItem) {
|
||||||
|
const roomId = safeText(pick(liveItem, [
|
||||||
|
'_live_room_id',
|
||||||
|
'room_id', 'roomId', 'roomid',
|
||||||
|
'live_room_id', 'liveRoomId',
|
||||||
|
'id', 'match_id', 'matchId'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const detailData = roomId ? await fetchLiveDetail(roomId) : null;
|
||||||
|
|
||||||
|
const title = safeText(pick(liveItem, [
|
||||||
|
'_live_title',
|
||||||
|
'title', 'name', 'match_name', 'matchName'
|
||||||
|
], '正在直播'));
|
||||||
|
|
||||||
|
const league = safeText(pick(liveItem, [
|
||||||
|
'_live_league',
|
||||||
|
'league_name', 'leagueName',
|
||||||
|
'league',
|
||||||
|
'competition_name', 'competitionName'
|
||||||
|
], '直播'));
|
||||||
|
|
||||||
|
const time = safeText(pick(liveItem, [
|
||||||
|
'_live_time',
|
||||||
|
'start_time', 'startTime',
|
||||||
|
'match_time', 'matchTime',
|
||||||
|
'time'
|
||||||
|
], '正在直播'));
|
||||||
|
|
||||||
|
const pic = fixUrl(pick(liveItem, [
|
||||||
|
'cover_image', 'coverImage',
|
||||||
|
'cover', 'thumb', 'thumbnail',
|
||||||
|
'home_team_logo', 'homeLogo',
|
||||||
|
'away_team_logo', 'awayLogo',
|
||||||
|
'logo', 'pic'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const playLine = buildLivePlayLine(liveItem, detailData);
|
||||||
|
|
||||||
|
const vodInfo = {
|
||||||
|
vod_id: rawId,
|
||||||
|
vod_name: title,
|
||||||
|
vod_pic: pic,
|
||||||
|
vod_remarks: '正在直播',
|
||||||
|
vod_content: `${league} ${title},比赛时间:${time}`,
|
||||||
|
vod_play_from: playLine ? '正在直播' : '',
|
||||||
|
vod_play_url: playLine,
|
||||||
|
type_name: league
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify({ list: [vodInfo] });
|
||||||
|
}
|
||||||
|
|
||||||
|
const matchId = rawId;
|
||||||
|
const data = await fetchMatchRecordings(matchId);
|
||||||
|
if (!isOk(data)) return JSON.stringify({ list: [] });
|
||||||
|
|
||||||
|
const root = data.data || {};
|
||||||
|
const match = root.match || {};
|
||||||
|
const replays = root.replays || [];
|
||||||
|
const highlights = root.highlights || [];
|
||||||
|
|
||||||
|
const home = safeText(pick(match, [
|
||||||
|
'home_team', 'homeTeam',
|
||||||
|
'home_name', 'homeName',
|
||||||
|
'team1', 'home'
|
||||||
|
], '主队'));
|
||||||
|
|
||||||
|
const away = safeText(pick(match, [
|
||||||
|
'away_team', 'awayTeam',
|
||||||
|
'away_name', 'awayName',
|
||||||
|
'team2', 'away'
|
||||||
|
], '客队'));
|
||||||
|
|
||||||
|
const league = safeText(pick(match, [
|
||||||
|
'league_name', 'leagueName',
|
||||||
|
'league',
|
||||||
|
'competition_name', 'competitionName'
|
||||||
|
], '赛事'));
|
||||||
|
|
||||||
|
const title = joinTitle(home, away, league);
|
||||||
|
|
||||||
|
const score = `${safeNumber(pick(match, ['home_score', 'homeScore', 'score_home'], '-'))} - ${safeNumber(pick(match, ['away_score', 'awayScore', 'score_away'], '-'))}`;
|
||||||
|
|
||||||
|
const time = safeText(pick(match, [
|
||||||
|
'start_time', 'startTime',
|
||||||
|
'match_time', 'matchTime',
|
||||||
|
'time'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const round = safeText(pick(match, ['match_round', 'matchRound', 'round'], ''));
|
||||||
|
|
||||||
|
const playFrom = [];
|
||||||
|
const playUrls = [];
|
||||||
|
|
||||||
|
const replayLine = buildEpisodeLine(replays, '录像');
|
||||||
|
if (replayLine) {
|
||||||
|
playFrom.push('全场录像');
|
||||||
|
playUrls.push(replayLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
const highlightLine = buildEpisodeLine(highlights, '集锦');
|
||||||
|
if (highlightLine) {
|
||||||
|
playFrom.push('比赛集锦');
|
||||||
|
playUrls.push(highlightLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
const pic = fixUrl(pick(match, [
|
||||||
|
'cover_image', 'coverImage',
|
||||||
|
'cover',
|
||||||
|
'home_team_logo', 'homeLogo',
|
||||||
|
'away_team_logo', 'awayLogo',
|
||||||
|
'logo', 'pic'
|
||||||
|
], ''));
|
||||||
|
|
||||||
|
const vodInfo = {
|
||||||
|
vod_id: matchId,
|
||||||
|
vod_name: title,
|
||||||
|
vod_pic: pic,
|
||||||
|
vod_remarks: score,
|
||||||
|
vod_content: `${league} ${round} ${home} vs ${away},比分 ${score},比赛时间:${time}`,
|
||||||
|
vod_play_from: playFrom.join('$$$'),
|
||||||
|
vod_play_url: playUrls.join('$$$'),
|
||||||
|
type_name: league
|
||||||
|
};
|
||||||
|
|
||||||
|
return JSON.stringify({ list: [vodInfo] });
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({ list: [] });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function search(wd, quick, pg = 1) {
|
||||||
|
try {
|
||||||
|
const keyword = safeText(wd).toLowerCase();
|
||||||
|
if (!keyword) {
|
||||||
|
return JSON.stringify({
|
||||||
|
page: 1,
|
||||||
|
pagecount: 0,
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const liveItems = await fetchLiveMatches(1, 100);
|
||||||
|
const filteredLive = liveItems.filter(item => {
|
||||||
|
return liveTitleText(item).toLowerCase().includes(keyword);
|
||||||
|
});
|
||||||
|
const liveList = parseLiveList(filteredLive);
|
||||||
|
|
||||||
|
const data = await fetchRecordings(1, 100);
|
||||||
|
let replayList = [];
|
||||||
|
|
||||||
|
if (isOk(data)) {
|
||||||
|
const arr = getDataArray(data);
|
||||||
|
const filteredReplay = arr.filter(item => {
|
||||||
|
const title = [
|
||||||
|
pick(item, ['title', 'name', 'match_name', 'matchName'], ''),
|
||||||
|
pick(item, ['home_team', 'homeTeam', 'home_name', 'homeName'], ''),
|
||||||
|
pick(item, ['away_team', 'awayTeam', 'away_name', 'awayName'], ''),
|
||||||
|
pick(item, ['league_name', 'leagueName', 'league'], '')
|
||||||
|
].join(' ').toLowerCase();
|
||||||
|
|
||||||
|
return title.includes(keyword);
|
||||||
|
});
|
||||||
|
|
||||||
|
replayList = parseVideoList(filteredReplay);
|
||||||
|
}
|
||||||
|
|
||||||
|
const list = uniqueByVodId([].concat(liveList, replayList));
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
page: parseInt(pg) || 1,
|
||||||
|
pagecount: 1,
|
||||||
|
limit: list.length,
|
||||||
|
total: list.length,
|
||||||
|
list: list
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
page: parseInt(pg) || 1,
|
||||||
|
pagecount: 0,
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function play(flag, id, flags) {
|
||||||
|
try {
|
||||||
|
const url = cleanPlayUrl(id);
|
||||||
|
const needParse = isLivePageUrl(url) && !isMediaUrl(url);
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
parse: needParse ? 1 : 0,
|
||||||
|
url: url,
|
||||||
|
header: {
|
||||||
|
'User-Agent': headers['User-Agent'],
|
||||||
|
'Referer': isLivePageUrl(url) ? host + '/pc/live' : host,
|
||||||
|
'Origin': host
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
parse: 0,
|
||||||
|
url: id || ''
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
init: init,
|
||||||
|
home: home,
|
||||||
|
homeVod: homeVod,
|
||||||
|
category: category,
|
||||||
|
detail: detail,
|
||||||
|
search: search,
|
||||||
|
play: play
|
||||||
|
};
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
var rule = {
|
||||||
|
title: '红果果短剧',
|
||||||
|
host: 'https://www.hongguoguo.tv/',
|
||||||
|
url: '/vod/show/id/fyclass/page/fyage.html',
|
||||||
|
searchUrl: '/vod/search/page/fypage/wd/**.html',
|
||||||
|
class_name: '精选短剧&穿越短剧&古装短剧&逆袭短剧&都市短剧',
|
||||||
|
class_url: 'jingxuanduanju&chuanyueduanju&guzhuangduanju&nixiduanju&dushiduanju',
|
||||||
|
searchable: 2,
|
||||||
|
quickSearch: 0,
|
||||||
|
filterable: 0,
|
||||||
|
headers: {
|
||||||
|
'User-Agent': 'MOBILE_UA',
|
||||||
|
},
|
||||||
|
play_parse: true,
|
||||||
|
lazy: "js:\n let html = request(input);\n let hconf = html.match(/r player_.*?=(.*?)</)[1];\n let json = JSON5.parse(hconf);\n let url = json.url;\n if (json.encrypt == '1') {\n url = unescape(url);\n } else if (json.encrypt == '2') {\n url = unescape(base64Decode(url));\n }\n if (/\\.(m3u8|mp4|m4a|mp3)/.test(url)) {\n input = {\n parse: 0,\n jx: 0,\n url: url,\n };\n } else {\n input;\n }",
|
||||||
|
limit: 6,
|
||||||
|
double: true,
|
||||||
|
推荐: '.cbox_list;*;*;*;*;*',
|
||||||
|
一级: '.flex.wrap li;.tim-title&&Text;.lazy&&data-original;.tim-tag&&Text;a&&href',
|
||||||
|
二级: {
|
||||||
|
title: 'h1.tim-title&&Text',
|
||||||
|
img: '.lazy&&data-original',
|
||||||
|
desc: '.title-head p&&Text;.play-detail a:eq(3)&&Text;;.play-detail p:eq(3)&&Text;.play-detail p:eq(2)&&Text',
|
||||||
|
content: '.play-detail p:eq(5)&&Text',
|
||||||
|
tabs: '.swiper-wrapper a',
|
||||||
|
lists: '.anthology-list li',
|
||||||
|
},
|
||||||
|
搜索: '.left li;.tim-title&&Text;.lazy&&data-original;.tim-tag&&Text;a&&href',
|
||||||
|
}
|
||||||
+620
@@ -0,0 +1,620 @@
|
|||||||
|
// 本资源来源于互联网公开渠道,仅可用于个人学习爬虫技术。
|
||||||
|
// 严禁将其用于任何商业用途,下载后请于 24 小时内删除,搜索结果均来自源站,本人不承担任何责任。
|
||||||
|
import {
|
||||||
|
Crypto as e
|
||||||
|
} from "assets://js/lib/cat.js";
|
||||||
|
let t, n = "https://web.88spa-03.com:8443",
|
||||||
|
r = "https://88api.omwjhz.com:18888";
|
||||||
|
const o = {
|
||||||
|
"User-Agent": "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36",
|
||||||
|
Accept: "application/json, text/plain, */*",
|
||||||
|
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||||
|
"Cache-Control": "no-cache",
|
||||||
|
Origin: n,
|
||||||
|
Pragma: "no-cache",
|
||||||
|
Referer: `${n}`,
|
||||||
|
"Sec-Fetch-Dest": "empty",
|
||||||
|
"Sec-Fetch-Mode": "cors",
|
||||||
|
"Sec-Fetch-Site": "cross-site",
|
||||||
|
"sec-ch-ua": '"Not/A)Brand";v="8", "Chromium";v="143", "Google Chrome";v="143"',
|
||||||
|
"sec-ch-ua-mobile": "?1",
|
||||||
|
"sec-ch-ua-platform": '"Android"'
|
||||||
|
},
|
||||||
|
a = "function" == typeof js2Proxy && "function" == typeof desX && "function" != typeof getProxy;
|
||||||
|
async function s(e) {}
|
||||||
|
async function i(e) {
|
||||||
|
let n = [{
|
||||||
|
type_id: "1",
|
||||||
|
type_name: "电影"
|
||||||
|
}, {
|
||||||
|
type_id: "2",
|
||||||
|
type_name: "连续剧"
|
||||||
|
}, {
|
||||||
|
type_id: "3",
|
||||||
|
type_name: "综艺"
|
||||||
|
}, {
|
||||||
|
type_id: "4",
|
||||||
|
type_name: "动漫"
|
||||||
|
}];
|
||||||
|
a && n.unshift({
|
||||||
|
type_id: "home",
|
||||||
|
type_name: "首页"
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
if (!e) return JSON.stringify({
|
||||||
|
class: n,
|
||||||
|
filters: {}
|
||||||
|
});
|
||||||
|
if (t) return JSON.stringify({
|
||||||
|
class: n,
|
||||||
|
filters: t
|
||||||
|
});
|
||||||
|
const a = `${r}/v1/vod-list-options`,
|
||||||
|
s = await req(a, {
|
||||||
|
headers: o,
|
||||||
|
timeout: 8e3
|
||||||
|
});
|
||||||
|
let i = {};
|
||||||
|
if (s && s.content) {
|
||||||
|
let e = "string" == typeof s.content ? JSON.parse(s.content) : s.content;
|
||||||
|
const n = e.areas || {},
|
||||||
|
r = e.types || {},
|
||||||
|
o = e.languages || [];
|
||||||
|
let a = [{
|
||||||
|
n: "全部",
|
||||||
|
v: "全部"
|
||||||
|
}];
|
||||||
|
Array.isArray(o) && o.forEach(e => {
|
||||||
|
"全部" !== e.name && a.push({
|
||||||
|
n: e.name,
|
||||||
|
v: e.name
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let c = [{
|
||||||
|
n: "全部",
|
||||||
|
v: ""
|
||||||
|
}];
|
||||||
|
["2026", "2025", "2024", "2023", "2022", "2021", "2020", "2019", "2018", "2017", "2016", "2015", "2014", "2013", "2012", "2011", "2010"].forEach(e => {
|
||||||
|
c.push({
|
||||||
|
n: e,
|
||||||
|
v: e
|
||||||
|
})
|
||||||
|
});
|
||||||
|
let p = [{
|
||||||
|
n: "全部",
|
||||||
|
v: "全部"
|
||||||
|
}];
|
||||||
|
for (let e = 65; e <= 90; e++) p.push({
|
||||||
|
n: String.fromCharCode(e),
|
||||||
|
v: String.fromCharCode(e)
|
||||||
|
});
|
||||||
|
for (let e = 0; e <= 9; e++) p.push({
|
||||||
|
n: e.toString(),
|
||||||
|
v: e.toString()
|
||||||
|
});
|
||||||
|
let l = [{
|
||||||
|
n: "最新",
|
||||||
|
v: "time"
|
||||||
|
}, {
|
||||||
|
n: "最热",
|
||||||
|
v: "hot"
|
||||||
|
}, {
|
||||||
|
n: "评分",
|
||||||
|
v: "score"
|
||||||
|
}];
|
||||||
|
const d = {
|
||||||
|
1: "电影",
|
||||||
|
2: "连续剧",
|
||||||
|
3: "综艺",
|
||||||
|
4: "动漫"
|
||||||
|
};
|
||||||
|
for (const e in d) {
|
||||||
|
const t = d[e];
|
||||||
|
let o = [],
|
||||||
|
s = [{
|
||||||
|
n: "全部",
|
||||||
|
v: e
|
||||||
|
}];
|
||||||
|
Array.isArray(r[t]) && r[t].forEach(e => {
|
||||||
|
"全部" !== e.name && s.push({
|
||||||
|
n: e.name,
|
||||||
|
v: e.id.toString()
|
||||||
|
})
|
||||||
|
}), o.push({
|
||||||
|
key: "cateId",
|
||||||
|
name: "类型",
|
||||||
|
init: e,
|
||||||
|
value: s
|
||||||
|
});
|
||||||
|
let u = [{
|
||||||
|
n: "全部",
|
||||||
|
v: "全部"
|
||||||
|
}];
|
||||||
|
Array.isArray(n[t]) && n[t].forEach(e => {
|
||||||
|
"全部" !== e.name && u.push({
|
||||||
|
n: e.name,
|
||||||
|
v: e.name
|
||||||
|
})
|
||||||
|
}), o.push({
|
||||||
|
key: "area",
|
||||||
|
name: "地区",
|
||||||
|
init: "全部",
|
||||||
|
value: u
|
||||||
|
}), o.push({
|
||||||
|
key: "lang",
|
||||||
|
name: "语言",
|
||||||
|
init: "全部",
|
||||||
|
value: a
|
||||||
|
}), o.push({
|
||||||
|
key: "year",
|
||||||
|
name: "年份",
|
||||||
|
init: "",
|
||||||
|
value: c
|
||||||
|
}), o.push({
|
||||||
|
key: "letter",
|
||||||
|
name: "字母",
|
||||||
|
init: "全部",
|
||||||
|
value: p
|
||||||
|
}), o.push({
|
||||||
|
key: "orderBy",
|
||||||
|
name: "排序",
|
||||||
|
init: "time",
|
||||||
|
value: l
|
||||||
|
}), i[e] = o
|
||||||
|
}
|
||||||
|
t = i
|
||||||
|
}
|
||||||
|
return JSON.stringify({
|
||||||
|
class: n,
|
||||||
|
filters: t || {}
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
class: n,
|
||||||
|
filters: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function c() {
|
||||||
|
try {
|
||||||
|
const e = `${r}/v1/home-list`,
|
||||||
|
t = await req(e, {
|
||||||
|
headers: o,
|
||||||
|
timeout: 8e3
|
||||||
|
});
|
||||||
|
if (!t || !t.content) return JSON.stringify({
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
let n = "string" == typeof t.content ? JSON.parse(t.content) : t.content,
|
||||||
|
a = new Map;
|
||||||
|
for (const e in n) Array.isArray(n[e]) && n[e].forEach(e => {
|
||||||
|
if (e && e.VodID) {
|
||||||
|
const t = e.VodID.toString();
|
||||||
|
a.has(t) || a.set(t, {
|
||||||
|
vod_id: t,
|
||||||
|
vod_name: e.VodName,
|
||||||
|
vod_pic: e.VodPic,
|
||||||
|
vod_remarks: e.VodRemarks
|
||||||
|
})
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const s = Array.from(a.values());
|
||||||
|
return JSON.stringify({
|
||||||
|
list: s
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
list: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function p(e, t, n, a) {
|
||||||
|
try {
|
||||||
|
if ("home" === e) {
|
||||||
|
let e = JSON.parse(await c());
|
||||||
|
return JSON.stringify({
|
||||||
|
list: e.list || [],
|
||||||
|
page: 1,
|
||||||
|
pagecount: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const n = a.cateId ? encodeURIComponent(a.cateId) : e,
|
||||||
|
s = "全部",
|
||||||
|
i = a.area ? encodeURIComponent(a.area) : "全部",
|
||||||
|
p = a.lang ? encodeURIComponent(a.lang) : "全部",
|
||||||
|
l = a.year ? encodeURIComponent(a.year) : "",
|
||||||
|
d = a.letter ? encodeURIComponent(a.letter) : "全部",
|
||||||
|
u = a.orderBy ? encodeURIComponent(a.orderBy) : "time",
|
||||||
|
g = `${r}/v1/vod-list?orderBy=${u}&type=${n}&class=${s}&area=${i}&year=${l}&lang=${p}&letter=${d}&page=${t}`,
|
||||||
|
f = await req(g, {
|
||||||
|
headers: o,
|
||||||
|
timeout: 8e3
|
||||||
|
});
|
||||||
|
if (!f || !f.content) return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(t),
|
||||||
|
pagecount: parseInt(t)
|
||||||
|
});
|
||||||
|
let y = "string" == typeof f.content ? JSON.parse(f.content) : f.content;
|
||||||
|
if (0 !== y.status || !y.data) return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(t),
|
||||||
|
pagecount: parseInt(t)
|
||||||
|
});
|
||||||
|
const m = y.data.map(e => ({
|
||||||
|
vod_id: e.VodID.toString(),
|
||||||
|
vod_name: e.VodName,
|
||||||
|
vod_pic: e.VodPic,
|
||||||
|
vod_remarks: e.VodRemarks
|
||||||
|
})),
|
||||||
|
h = y.qty || 0,
|
||||||
|
v = h > 0 ? Math.ceil(h / 60) : parseInt(t);
|
||||||
|
return JSON.stringify({
|
||||||
|
list: m,
|
||||||
|
page: parseInt(t),
|
||||||
|
pagecount: v
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(t),
|
||||||
|
pagecount: parseInt(t)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function l(e, t, n = 1) {
|
||||||
|
try {
|
||||||
|
const t = encodeURIComponent(e),
|
||||||
|
a = `${r}/v1/search?keyword=${t}&cate=undefined&page=${n}`,
|
||||||
|
s = await req(a, {
|
||||||
|
headers: o,
|
||||||
|
timeout: 8e3
|
||||||
|
});
|
||||||
|
if (!s || !s.content) return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(n),
|
||||||
|
pagecount: parseInt(n)
|
||||||
|
});
|
||||||
|
let i = "string" == typeof s.content ? JSON.parse(s.content) : s.content;
|
||||||
|
if (0 !== i.status || !i.data) return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(n),
|
||||||
|
pagecount: parseInt(n)
|
||||||
|
});
|
||||||
|
const c = i.data.map(e => ({
|
||||||
|
vod_id: e.VodID.toString(),
|
||||||
|
vod_name: e.VodName,
|
||||||
|
vod_pic: e.VodPic,
|
||||||
|
vod_remarks: e.VodRemarks
|
||||||
|
})),
|
||||||
|
p = i.qty || 0,
|
||||||
|
l = p > 0 ? Math.ceil(p / 60) : parseInt(n);
|
||||||
|
return JSON.stringify({
|
||||||
|
list: c,
|
||||||
|
page: parseInt(n),
|
||||||
|
pagecount: l
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
list: [],
|
||||||
|
page: parseInt(n),
|
||||||
|
pagecount: parseInt(n)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function d(e) {
|
||||||
|
try {
|
||||||
|
const t = `${r}/v1/vod-details?id=${e}`,
|
||||||
|
n = await req(t, {
|
||||||
|
headers: o,
|
||||||
|
timeout: 8e3
|
||||||
|
});
|
||||||
|
if (!n || !n.content) return JSON.stringify({
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
let a = "string" == typeof n.content ? JSON.parse(n.content) : n.content;
|
||||||
|
if (0 !== a.status || !a.vod) return JSON.stringify({
|
||||||
|
list: []
|
||||||
|
});
|
||||||
|
const s = a.vod,
|
||||||
|
i = s.VodPlayServer || [],
|
||||||
|
c = s.VodPlayUrls || {};
|
||||||
|
let p = [],
|
||||||
|
l = [];
|
||||||
|
i.forEach(e => {
|
||||||
|
if (1 === e.Status) {
|
||||||
|
const t = e.From,
|
||||||
|
n = e.Show,
|
||||||
|
r = c[t] || [];
|
||||||
|
if (r.length > 0) {
|
||||||
|
const e = r.map(e => `${e[0]}$${e[1]}`).join("#");
|
||||||
|
p.push(n), l.push(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const d = p.join("$$$"),
|
||||||
|
u = l.join("$$$"),
|
||||||
|
g = {
|
||||||
|
vod_id: s.VodID.toString(),
|
||||||
|
vod_name: s.VodName,
|
||||||
|
vod_pic: s.VodPic,
|
||||||
|
type_name: a.VodClass ? a.VodClass.TypeName : "",
|
||||||
|
vod_year: s.VodYear || "",
|
||||||
|
vod_area: s.VodArea || "",
|
||||||
|
vod_remarks: s.VodRemarks || "",
|
||||||
|
vod_actor: s.VodActor || "",
|
||||||
|
vod_director: s.VodDirector || "",
|
||||||
|
vod_content: s.VodContent ? s.VodContent.replace(/<\/?[^>]+(>|$)/g, "") : "",
|
||||||
|
vod_play_from: d,
|
||||||
|
vod_play_url: u
|
||||||
|
};
|
||||||
|
return JSON.stringify({
|
||||||
|
list: [g]
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
return JSON.stringify({
|
||||||
|
list: []
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async function u(t, r, a) {
|
||||||
|
try {
|
||||||
|
const t = /^(https?:)?\/\//i.test(r) ? r : `https://vip.jsjinfu.com:8443/?url=${r}`;
|
||||||
|
let a = await async function(t, n) {
|
||||||
|
let r = {
|
||||||
|
"User-Agent": o["User-Agent"],
|
||||||
|
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",
|
||||||
|
referer: n,
|
||||||
|
"upgrade-insecure-requests": "1"
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
const n = (await req(t, {
|
||||||
|
headers: r
|
||||||
|
})).content,
|
||||||
|
a = h(n, "video_url");
|
||||||
|
if (a) return a;
|
||||||
|
const s = h(n, "Domain"),
|
||||||
|
i = h(n, "Time"),
|
||||||
|
c = h(n, "Version"),
|
||||||
|
p = h(n, "Vurl"),
|
||||||
|
l = h(n, "Vkey"),
|
||||||
|
d = h(n, "Key"),
|
||||||
|
u = h(n, "Ref"),
|
||||||
|
S = s || "vip.jsjinfu.com";
|
||||||
|
if (!(i && p && l && d)) return null;
|
||||||
|
const $ = md5X(l);
|
||||||
|
let _ = y(d, $),
|
||||||
|
N = g.encryptToBase64(d, _);
|
||||||
|
const k = s + S + i + p + d + _;
|
||||||
|
let O = m(md5X(k)),
|
||||||
|
C = f.encode(O, "971a0e7224fecb61b1868f1211a6360d"),
|
||||||
|
A = y(C, $),
|
||||||
|
I = g.encryptToBase64(C, A),
|
||||||
|
V = m(md5X(k + C + A)),
|
||||||
|
b = f.encode(V, i + "52bc46dec47a199abc82793bfabe56f6"),
|
||||||
|
J = y(b, $),
|
||||||
|
w = g.encryptToBase64(b, J),
|
||||||
|
U = `${l}-${d}-${C}-${b}`,
|
||||||
|
E = `${_}-${A}-${J}`;
|
||||||
|
const D = U + E;
|
||||||
|
let R = g.encryptToBase64(D + N + I + w, S + s + i),
|
||||||
|
B = {
|
||||||
|
url: p,
|
||||||
|
wap: "0",
|
||||||
|
ios: "0",
|
||||||
|
host: S,
|
||||||
|
referer: u,
|
||||||
|
time: i,
|
||||||
|
key: d,
|
||||||
|
key1: _,
|
||||||
|
sign: C,
|
||||||
|
sign1: A,
|
||||||
|
token: b,
|
||||||
|
token1: J
|
||||||
|
},
|
||||||
|
X = function(t, n) {
|
||||||
|
let r = e.enc.Latin1.parse(n.substring(0, 16)),
|
||||||
|
o = e.enc.Latin1.parse(n.substring(16, 32));
|
||||||
|
return e.AES.encrypt(e.enc.Base64.stringify(e.enc.Utf8.parse(t)), r, {
|
||||||
|
iv: o,
|
||||||
|
mode: e.mode.CBC,
|
||||||
|
padding: e.pad.Pkcs7
|
||||||
|
}).toString()
|
||||||
|
}(JSON.stringify(B), md5X(R));
|
||||||
|
B.ckey = "110#" + e.enc.Base64.stringify(e.enc.Latin1.parse(X));
|
||||||
|
let q = function(e) {
|
||||||
|
let t = String(e.time),
|
||||||
|
n = e.key,
|
||||||
|
r = e.key1,
|
||||||
|
o = e.sign1,
|
||||||
|
a = e.token1,
|
||||||
|
s = ["2", "6", "2", "4"],
|
||||||
|
i = t.split("")[s[0]] || "e",
|
||||||
|
c = n.split("")[s[1]] || "t",
|
||||||
|
p = r.split("")[s[2]] || "c",
|
||||||
|
l = o.split("")[o.split("").length - s[3]] || "n",
|
||||||
|
d = a.split(""),
|
||||||
|
u = [];
|
||||||
|
for (let e = 0; 2 * e < d.length; e++) switch (u.push(d[d.length - e - 1]), e < d.length - e - 1 && u.push(a[e]), e) {
|
||||||
|
case 1:
|
||||||
|
u.push(i);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
u.push(c);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
u.push(p);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
u.push(l)
|
||||||
|
}
|
||||||
|
return u.join("")
|
||||||
|
}(B),
|
||||||
|
T = 1024 * i,
|
||||||
|
j = `https://${s}:8443/Api.php?ver=${c}×tamp=${T}&appkey=${md5X(S+T+c)}`,
|
||||||
|
x = {
|
||||||
|
"User-Agent": o["User-Agent"],
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||||
|
Vkey: l,
|
||||||
|
Md5: md5X(D + R + q),
|
||||||
|
Version: c,
|
||||||
|
"Access-Token0": U,
|
||||||
|
"Access-Token1": E,
|
||||||
|
"Access-Token2": R,
|
||||||
|
"Access-Token3": q,
|
||||||
|
Origin: `https://${S}:8443`,
|
||||||
|
Referer: t
|
||||||
|
};
|
||||||
|
const M = await req(j, {
|
||||||
|
method: "post",
|
||||||
|
headers: x,
|
||||||
|
data: v(B),
|
||||||
|
postType: "form"
|
||||||
|
});
|
||||||
|
let P = "string" == typeof M.content ? JSON.parse(M.content) : M.content;
|
||||||
|
if (P && 1 === P.Status) {
|
||||||
|
let t = P.Appkey + P.Md5 + P.Version,
|
||||||
|
n = md5X(t),
|
||||||
|
r = e.enc.Latin1.parse(n.substring(0, 16)),
|
||||||
|
o = e.enc.Latin1.parse(n.substring(16, 32)),
|
||||||
|
a = e.AES.decrypt(P.Data, r, {
|
||||||
|
iv: o,
|
||||||
|
mode: e.mode.CBC,
|
||||||
|
padding: e.pad.Pkcs7
|
||||||
|
}),
|
||||||
|
s = JSON.parse(e.enc.Utf8.stringify(a)),
|
||||||
|
i = f.decode(s.url, md5X(S + b));
|
||||||
|
return decodeURIComponent(i)
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
return null
|
||||||
|
}(t, `${n}/`);
|
||||||
|
if (a) return JSON.stringify({
|
||||||
|
parse: 0,
|
||||||
|
url: a,
|
||||||
|
header: {
|
||||||
|
"User-Agent": o["User-Agent"]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} catch (e) {}
|
||||||
|
return JSON.stringify({
|
||||||
|
parse: 1,
|
||||||
|
url: r,
|
||||||
|
header: {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const g = (e => {
|
||||||
|
if (!e) throw new Error("");
|
||||||
|
const t = (e, t) => {
|
||||||
|
const n = e.words,
|
||||||
|
r = e.sigBytes,
|
||||||
|
o = r + 3 >> 2,
|
||||||
|
a = new Array(t ? o + 1 : o);
|
||||||
|
for (let e = 0; e < r; e++) a[e >> 2] |= (n[e >> 2] >>> 24 - e % 4 * 8 & 255) << ((3 & e) << 3);
|
||||||
|
return t && (a[o] = r), a
|
||||||
|
},
|
||||||
|
n = (e, t, n, r, o, a) => (n >>> 5 ^ t << 2) + (t >>> 3 ^ n << 4) ^ (e ^ t) + (a[3 & r ^ o] ^ n);
|
||||||
|
return {
|
||||||
|
encryptToBase64(r, o) {
|
||||||
|
if (!r || 0 === r.length) return r;
|
||||||
|
const a = t(e.enc.Utf8.parse(r), !0),
|
||||||
|
s = t(e.enc.Utf8.parse(o), !1);
|
||||||
|
return e.enc.Base64.stringify((t => {
|
||||||
|
const n = t.length,
|
||||||
|
r = [];
|
||||||
|
for (let e = 0; e < n; e++) {
|
||||||
|
const n = t[e];
|
||||||
|
r[e] = (255 & n) << 24 | (n >>> 8 & 255) << 16 | (n >>> 16 & 255) << 8 | n >>> 24
|
||||||
|
}
|
||||||
|
return e.lib.WordArray.create(r, 4 * n)
|
||||||
|
})(((e, t) => {
|
||||||
|
t.length < 4 && (t.length = 4);
|
||||||
|
const r = e.length,
|
||||||
|
o = r - 1;
|
||||||
|
let a, s = e[o],
|
||||||
|
i = 0;
|
||||||
|
for (let c = 0 | Math.floor(6 + 52 / r); c > 0; --c) {
|
||||||
|
i = i + 2654435769 & 4294967295;
|
||||||
|
const r = i >>> 2 & 3;
|
||||||
|
for (let c = 0; c < o; ++c) a = e[c + 1], s = e[c] = e[c] + n(i, a, s, c, r, t) & 4294967295;
|
||||||
|
a = e[0], s = e[o] = e[o] + n(i, a, s, o, r, t) & 4294967295
|
||||||
|
}
|
||||||
|
return e
|
||||||
|
})(a, s)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})(void 0 !== e ? e : null),
|
||||||
|
f = function() {
|
||||||
|
const t = () => Math.floor(Date.now() / 1e3),
|
||||||
|
n = function(n, r = "DECODE", o = "liangcheng", a = 0) {
|
||||||
|
o = md5X(o);
|
||||||
|
const s = md5X(o.substring(0, 16)),
|
||||||
|
i = md5X(o.substring(16, 32));
|
||||||
|
let c = "";
|
||||||
|
if ("DECODE" === r) c = n.substring(0, 4);
|
||||||
|
else {
|
||||||
|
const e = md5X((() => {
|
||||||
|
const e = Date.now(),
|
||||||
|
t = Math.floor(e / 1e3);
|
||||||
|
return (e - 1e3 * t) / 1e3 + " " + t
|
||||||
|
})());
|
||||||
|
c = e.substring(e.length - 4)
|
||||||
|
}
|
||||||
|
const p = e.enc.Utf8.parse(s + md5X(s + c));
|
||||||
|
let l = "";
|
||||||
|
if ("DECODE" === r) {
|
||||||
|
let r = e.enc.Base64.parse(n.substring(4)),
|
||||||
|
o = e.RC4.decrypt({
|
||||||
|
ciphertext: r
|
||||||
|
}, p);
|
||||||
|
l = e.enc.Latin1.stringify(o);
|
||||||
|
const a = parseInt(l.substring(0, 10), 10),
|
||||||
|
s = 0 === a || a - t() > 0,
|
||||||
|
c = l.substring(10, 26) === md5X(l.substring(26) + i).substring(0, 16);
|
||||||
|
return s && c ? l.substring(26) : ""
|
||||||
|
} {
|
||||||
|
a = a ? a + t() : 0;
|
||||||
|
const r = String(a).padStart(10, "0") + md5X(n + i).substring(0, 16) + n;
|
||||||
|
return l = e.RC4.encrypt(e.enc.Latin1.parse(r), p).toString(), c + l.replace(/=/g, "")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
encode: (e, t, r) => n(e, "ENCODE", t, r).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "."),
|
||||||
|
decode: (e, t) => n(e.replace(/-/g, "+").replace(/_/g, "/").replace(/\./g, "="), "DECODE", t)
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
|
||||||
|
function y(t, n) {
|
||||||
|
return e.AES.encrypt(e.enc.Utf8.parse(t), e.enc.Utf8.parse(n), {
|
||||||
|
mode: e.mode.ECB
|
||||||
|
}).ciphertext.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function m(e) {
|
||||||
|
return md5X(`17325841932717338791732584194271733878${e}`.replace(/[\-|\,]/g, ""))
|
||||||
|
}
|
||||||
|
|
||||||
|
function h(e, t) {
|
||||||
|
const n = e.match(new RegExp(`var\\s+${t}\\s*=\\s*['"]([^'"]+)['"]`));
|
||||||
|
return n ? n[1] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function v(e) {
|
||||||
|
let t = {};
|
||||||
|
for (let n in e) t[n] = a ? encodeURIComponent(e[n] || "") : e[n];
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
export function __jsEvalReturn() {
|
||||||
|
return {
|
||||||
|
init: s,
|
||||||
|
home: i,
|
||||||
|
homeVod: c,
|
||||||
|
category: p,
|
||||||
|
search: l,
|
||||||
|
detail: d,
|
||||||
|
play: u
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
/*
|
||||||
|
@header({
|
||||||
|
searchable: 1,
|
||||||
|
filterable: 1,
|
||||||
|
quickSearch: 1,
|
||||||
|
title: '麻豆HD',
|
||||||
|
lang: 'cat'
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { load } from 'assets://js/lib/cat.js';
|
||||||
|
|
||||||
|
let host = 'https://www.madouhd.com';
|
||||||
|
let headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (Linux; Android 16; 23078RKD5C) AppleWebKit/537.36',
|
||||||
|
'Referer': host
|
||||||
|
};
|
||||||
|
|
||||||
|
async function request(url, options = {}) {
|
||||||
|
try {
|
||||||
|
let res = await req(url, { headers: { ...headers, ...options.headers }, ...options });
|
||||||
|
return res.content || '';
|
||||||
|
} catch (e) {
|
||||||
|
console.log('[请求失败] ' + url);
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildCategoryUrl(tid, page, extend) {
|
||||||
|
let cls = (extend.class || '').trim();
|
||||||
|
let year = (extend.year || '').trim();
|
||||||
|
let hasFilter = cls || year;
|
||||||
|
|
||||||
|
if (!hasFilter) {
|
||||||
|
return `${host}/vodtype/${tid}-${page}.html`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let clsPart = cls ? encodeURIComponent(cls) : '';
|
||||||
|
let yearPart = year ? year : '';
|
||||||
|
|
||||||
|
let base = `${host}/vodshow/${tid}---${clsPart}-----${page}`;
|
||||||
|
if (yearPart) {
|
||||||
|
base += `---${yearPart}`;
|
||||||
|
}
|
||||||
|
return base + '.html';
|
||||||
|
}
|
||||||
|
|
||||||
|
function yearOptions() {
|
||||||
|
let arr = [{ n: '全部', v: '' }];
|
||||||
|
for (let y = 2026; y >= 2004; y--) arr.push({ n: y.toString(), v: y.toString() });
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function home(filter) {
|
||||||
|
const classes = [
|
||||||
|
{ type_id: '1', type_name: '电影' },
|
||||||
|
{ type_id: '2', type_name: '电视剧' },
|
||||||
|
{ type_id: '3', type_name: '综艺' },
|
||||||
|
{ type_id: '4', type_name: '动漫' },
|
||||||
|
{ type_id: '24', type_name: '纪录片' }
|
||||||
|
];
|
||||||
|
|
||||||
|
const filters = {
|
||||||
|
'1': [
|
||||||
|
{ key: 'class', name: '剧情', value: [
|
||||||
|
{ n: '全部', v: '' }, { n: '动作', v: '动作' }, { n: '喜剧', v: '喜剧' },
|
||||||
|
{ n: '爱情', v: '爱情' }, { n: '科幻', v: '科幻' }, { n: '恐怖', v: '恐怖' },
|
||||||
|
{ n: '剧情', v: '剧情' }, { n: '战争', v: '战争' }, { n: '犯罪', v: '犯罪' }
|
||||||
|
]},
|
||||||
|
{ key: 'year', name: '年份', value: yearOptions() }
|
||||||
|
],
|
||||||
|
'2': [
|
||||||
|
{ key: 'class', name: '剧情', value: [
|
||||||
|
{ n: '全部', v: '' }, { n: '古装', v: '古装' }, { n: '爱情', v: '爱情' },
|
||||||
|
{ n: '奇幻', v: '奇幻' }, { n: '喜剧', v: '喜剧' }, { n: '家庭', v: '家庭' }
|
||||||
|
]},
|
||||||
|
{ key: 'year', name: '年份', value: yearOptions() }
|
||||||
|
],
|
||||||
|
'3': [{ key: 'year', name: '年份', value: yearOptions() }],
|
||||||
|
'4': [{ key: 'year', name: '年份', value: yearOptions() }],
|
||||||
|
'24': [{ key: 'year', name: '年份', value: yearOptions() }]
|
||||||
|
};
|
||||||
|
return JSON.stringify({ class: classes, filters: filters });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function homeVod() {
|
||||||
|
return JSON.stringify({ list: [] });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function category(tid, pg, filter, extend) {
|
||||||
|
let page = parseInt(pg) || 1;
|
||||||
|
let url = buildCategoryUrl(tid, page, extend || {});
|
||||||
|
console.log('[分类] ' + url);
|
||||||
|
|
||||||
|
let html = await request(url);
|
||||||
|
if (!html) return JSON.stringify({ list: [], page, pagecount: 1 });
|
||||||
|
|
||||||
|
const $ = load(html);
|
||||||
|
const list = [];
|
||||||
|
|
||||||
|
$('.stui-vodlist li').each((i, el) => {
|
||||||
|
let a = $(el).find('a.stui-vodlist__thumb');
|
||||||
|
let href = a.attr('href') || '';
|
||||||
|
let title = a.attr('title') || '';
|
||||||
|
let pic = a.attr('data-original') || a.find('img').attr('src') || '';
|
||||||
|
let remarks = a.find('.pic-text').text().trim();
|
||||||
|
let vid = (href.match(/\/(\d+)\.html$/) || [])[1] || href;
|
||||||
|
|
||||||
|
if (title && vid) {
|
||||||
|
list.push({
|
||||||
|
vod_id: vid,
|
||||||
|
vod_name: title,
|
||||||
|
vod_pic: pic,
|
||||||
|
vod_remarks: remarks
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let totalPage = 1;
|
||||||
|
$('a').each((i, el) => {
|
||||||
|
let href = $(el).attr('href') || '';
|
||||||
|
let m = href.match(/\/(\d+)(?:-(\d+)|-----(\d+)---\d+)?\.html$/);
|
||||||
|
if (m) {
|
||||||
|
let p = parseInt(m[2] || m[3] || m[1]);
|
||||||
|
if (p > totalPage) totalPage = p;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (totalPage < page && list.length > 0) totalPage = page + 1;
|
||||||
|
|
||||||
|
return JSON.stringify({
|
||||||
|
list,
|
||||||
|
page: page,
|
||||||
|
pagecount: totalPage,
|
||||||
|
limit: list.length,
|
||||||
|
total: totalPage * list.length
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function detail(id) {
|
||||||
|
let url = `${host}/dy/${id}.html`;
|
||||||
|
let html = await request(url);
|
||||||
|
if (!html) return JSON.stringify({ list: [] });
|
||||||
|
|
||||||
|
const $ = load(html);
|
||||||
|
let vod = {
|
||||||
|
vod_id: id,
|
||||||
|
vod_name: $('h1.title').text().trim(),
|
||||||
|
vod_pic: $('.stui-content__thumb img').attr('data-original') || $('.stui-content__thumb img').attr('src') || '',
|
||||||
|
vod_content: '',
|
||||||
|
vod_play_from: '',
|
||||||
|
vod_play_url: '',
|
||||||
|
vod_director: '',
|
||||||
|
vod_actor: '',
|
||||||
|
type_name: '',
|
||||||
|
vod_area: '',
|
||||||
|
vod_year: '',
|
||||||
|
vod_remarks: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
let dataText = $('.stui-content__detail .data').first().text() || '';
|
||||||
|
let typeMatch = dataText.match(/类型:(.+?)(?=\s*(?:地区|年份|$))/);
|
||||||
|
if (typeMatch) vod.type_name = typeMatch[1].replace(/\s+/g, ' ').trim();
|
||||||
|
let areaMatch = dataText.match(/地区:(.+?)(?=\s*(?:年份|$))/);
|
||||||
|
if (areaMatch) vod.vod_area = areaMatch[1].trim();
|
||||||
|
let yearMatch = dataText.match(/年份:(\d{4})/);
|
||||||
|
if (yearMatch) vod.vod_year = yearMatch[1];
|
||||||
|
$('.stui-content__detail .data').each((i, el) => {
|
||||||
|
let text = $(el).text();
|
||||||
|
if (text.includes('导演:'))
|
||||||
|
vod.vod_director = $(el).find('a').map((j, a) => $(a).text()).get().join(',');
|
||||||
|
if (text.includes('主演:'))
|
||||||
|
vod.vod_actor = $(el).find('a').map((j, a) => $(a).text()).get().join(',');
|
||||||
|
});
|
||||||
|
|
||||||
|
vod.vod_content = $('.detail-content').text().trim() || $('.detail-sketch').text().trim();
|
||||||
|
|
||||||
|
let updateMatch = $('.stui-content__detail').text().match(/更新:(\d{4}-\d{2}-\d{2})/);
|
||||||
|
if (updateMatch) vod.vod_remarks = updateMatch[1];
|
||||||
|
|
||||||
|
let sources = [];
|
||||||
|
$('.stui-pannel-box.b.playlist.mb').each((i, panel) => {
|
||||||
|
let name = $(panel).find('h3.title').text().trim();
|
||||||
|
let urls = [];
|
||||||
|
$(panel).find('.stui-content__playlist a').each((j, a) => {
|
||||||
|
let href = $(a).attr('href') || '';
|
||||||
|
let text = $(a).text().trim();
|
||||||
|
if (!href.startsWith('http')) href = host + href;
|
||||||
|
urls.push(text + '$' + href);
|
||||||
|
});
|
||||||
|
if (urls.length) sources.push({ name, urls: urls.join('#') });
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sources.length) {
|
||||||
|
vod.vod_play_from = sources.map(s => s.name).join('$$$');
|
||||||
|
vod.vod_play_url = sources.map(s => s.urls).join('$$$');
|
||||||
|
} else {
|
||||||
|
let btn = $('.play-btn .btn-primary').attr('href');
|
||||||
|
if (btn) {
|
||||||
|
if (!btn.startsWith('http')) btn = host + btn;
|
||||||
|
vod.vod_play_from = '播放';
|
||||||
|
vod.vod_play_url = '立即观看$' + btn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return JSON.stringify({ list: [vod] });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function search(wd, quick, pg) {
|
||||||
|
let page = pg || 1;
|
||||||
|
let url = `${host}/vodsearch/-------------.html?wd=${encodeURIComponent(wd)}&submit=`;
|
||||||
|
let html = await request(url);
|
||||||
|
if (!html) return JSON.stringify({ list: [] });
|
||||||
|
|
||||||
|
const $ = load(html);
|
||||||
|
const list = [];
|
||||||
|
$('.stui-vodlist li').each((i, el) => {
|
||||||
|
let a = $(el).find('a.stui-vodlist__thumb');
|
||||||
|
let href = a.attr('href') || '';
|
||||||
|
let title = a.attr('title') || '';
|
||||||
|
let pic = a.attr('data-original') || a.find('img').attr('src') || '';
|
||||||
|
let remarks = a.find('.pic-text').text().trim();
|
||||||
|
let vid = (href.match(/\/(\d+)\.html$/) || [])[1] || href;
|
||||||
|
if (title && vid) list.push({ vod_id: vid, vod_name: title, vod_pic: pic, vod_remarks: remarks });
|
||||||
|
});
|
||||||
|
return JSON.stringify({ list });
|
||||||
|
}
|
||||||
|
|
||||||
|
async function play(flag, id, flags) {
|
||||||
|
let url = id.startsWith('http') ? id : host + id;
|
||||||
|
let html = await request(url);
|
||||||
|
if (!html) return JSON.stringify({ parse: 0, url: url });
|
||||||
|
|
||||||
|
let match = html.match(/player_aaaa\s*=\s*(\{[\s\S]*?\});/);
|
||||||
|
if (match) {
|
||||||
|
try {
|
||||||
|
let cfg = JSON.parse(match[1]);
|
||||||
|
if (cfg.url) return JSON.stringify({ parse: 1, url: cfg.url, header: JSON.stringify({ Referer: url }) });
|
||||||
|
} catch (e) {}
|
||||||
|
let urlMatch = match[1].match(/"url":"([^"]+)"/);
|
||||||
|
if (urlMatch) {
|
||||||
|
let realUrl = urlMatch[1].replace(/\\\//g, '/');
|
||||||
|
return JSON.stringify({ parse: 1, url: realUrl, header: JSON.stringify({ Referer: url }) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return JSON.stringify({ parse: 0, url: url });
|
||||||
|
}
|
||||||
|
|
||||||
|
export function __jsEvalReturn() {
|
||||||
|
return { init: () => {}, home, homeVod, category, detail, play, search };
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user