From e80e63704cf0c886fc9449f11196cb63f724ed9c Mon Sep 17 00:00:00 2001 From: XC-Pro <19840+xc-pro@noreply.example.org> Date: Sat, 15 Aug 2026 10:27:57 +0200 Subject: [PATCH] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E6=96=87=E4=BB=B6=E8=87=B3?= =?UTF-8?q?=E3=80=8Cjs=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- js/体育聚合.js | 1659 ++++++++++++++++++++++++++++++++++++++++++++++++ js/咖啡体育.js | 1119 ++++++++++++++++++++++++++++++++ js/非凡.js | 141 ++++ js/高清剧迷.js | 620 ++++++++++++++++++ 4 files changed, 3539 insertions(+) create mode 100644 js/体育聚合.js create mode 100644 js/咖啡体育.js create mode 100644 js/非凡.js create mode 100644 js/高清剧迷.js diff --git a/js/体育聚合.js b/js/体育聚合.js new file mode 100644 index 0000000..1ea2efe --- /dev/null +++ b/js/体育聚合.js @@ -0,0 +1,1659 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 0, + title: '⚽ 体育cat|聚合', + author: 'OpenClaw', + lang: 'cat', + style: { type: 'rect', ratio: 0.75 } +}) +*/ + + +const __sports_src_0 = (function () { + + +let host = 'https://www.88kanqiu.la'; +const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'; +const headers = { + 'User-Agent': UA, + 'Referer': host + '/', + 'Accept': 'text/html,application/json,*/*' +}; + +const B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +function stripHtml(s) { + return String(s || '') + .replace(//gi, '') + .replace(//gi, '') + .replace(/<[^>]+>/g, '') + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, ' ') + .trim(); +} + +function absUrl(url) { + url = String(url || '').trim(); + if (!url) return ''; + if (/^https?:\/\//i.test(url)) return url; + if (url.indexOf('//') === 0) return 'https:' + url; + if (url.charAt(0) === '/') return host + url; + return host + '/' + url; +} + +function safeJson(text, def) { + try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } +} + +// TV 端 JS 环境不一定有 atob/TextDecoder;这里按 PHP 源的 base64_decode 逻辑手写 UTF-8 解码。 +function base64ToUtf8(str) { + str = String(str || '').replace(/[^A-Za-z0-9+/=]/g, ''); + const bytes = []; + for (let i = 0; i < str.length; i += 4) { + const c1 = B64_CHARS.indexOf(str.charAt(i)); + const c2 = B64_CHARS.indexOf(str.charAt(i + 1)); + const c3 = B64_CHARS.indexOf(str.charAt(i + 2)); + const c4 = B64_CHARS.indexOf(str.charAt(i + 3)); + if (c1 < 0 || c2 < 0) continue; + const n = (c1 << 18) | (c2 << 12) | ((c3 < 0 ? 0 : c3) << 6) | (c4 < 0 ? 0 : c4); + bytes.push((n >> 16) & 255); + if (str.charAt(i + 2) !== '=') bytes.push((n >> 8) & 255); + if (str.charAt(i + 3) !== '=') bytes.push(n & 255); + } + + let out = ''; + for (let i = 0; i < bytes.length;) { + const b1 = bytes[i++]; + if (b1 < 0x80) { + out += String.fromCharCode(b1); + } else if (b1 >= 0xc0 && b1 < 0xe0) { + const b2 = bytes[i++] || 0; + out += String.fromCharCode(((b1 & 0x1f) << 6) | (b2 & 0x3f)); + } else if (b1 >= 0xe0 && b1 < 0xf0) { + const b2 = bytes[i++] || 0; + const b3 = bytes[i++] || 0; + out += String.fromCharCode(((b1 & 0x0f) << 12) | ((b2 & 0x3f) << 6) | (b3 & 0x3f)); + } else { + const b2 = bytes[i++] || 0; + const b3 = bytes[i++] || 0; + const b4 = bytes[i++] || 0; + const cp = ((b1 & 0x07) << 18) | ((b2 & 0x3f) << 12) | ((b3 & 0x3f) << 6) | (b4 & 0x3f); + const u = cp - 0x10000; + out += String.fromCharCode(0xd800 + (u >> 10), 0xdc00 + (u & 0x3ff)); + } + } + return out; +} + +function decodePlaySource(raw) { + raw = String(raw || '').trim(); + if (!raw) return null; + for (let prefix = 0; prefix <= 8; prefix++) { + for (let suffix = 0; suffix <= 4; suffix++) { + if (raw.length <= prefix + suffix) continue; + const candidate = raw.slice(prefix, raw.length - suffix); + try { + const decoded = base64ToUtf8(candidate); + const pos = decoded.indexOf('{'); + if (pos < 0) continue; + const obj = JSON.parse(decoded.slice(pos)); + if (obj && typeof obj === 'object') return obj; + } catch (e) {} + } + } + return null; +} + +function toDirectPlayUrl(url) { + url = String(url || '').trim(); + if (!url) return ''; + const m = url.match(/[?&]url=([^&]+)/); + if (m && m[1]) { + try { return decodeURIComponent(m[1]); } catch (e) { return m[1]; } + } + return url; +} + +async function fetchText(url, referer) { + const hd = { + 'User-Agent': UA, + 'Referer': referer || host + '/', + 'Accept': 'text/html,application/json,*/*' + }; + + // 部分壳只有 Java.req,部分壳只有 req;两种都兼容。 + if (typeof Java !== 'undefined' && Java && Java.req) { + const r = Java.req(url, { headers: hd }); + return String((r && (r.body || r.content)) || ''); + } + const r2 = await req(url, { headers: hd }); + return String((r2 && (r2.content || r2.body)) || ''); +} + +function getClasses() { + return [ + { type_id: 'live', type_name: '正在直播' }, + { type_id: '0', type_name: '全部直播' }, + { type_id: '1', type_name: '篮球直播' }, + { type_id: '8', type_name: '足球直播' }, + { type_id: '21', type_name: '其他直播' } + ]; +} + +function parseList(html, onlyLiving) { + const list = []; + const reg = /]*class=["'][^"']*list-group-item[^"']*["'][^>]*>[\s\S]*?<\/li>/gi; + let m; + while ((m = reg.exec(String(html || ''))) !== null) { + const item = m[0]; + const hrefMatch = item.match(/]+href=["']([^"']*\/live\/\d+\/play[^"']*)["'][^>]*>/i); + if (!hrefMatch) continue; + + const time = stripHtml((item.match(/class=["'][^"']*category-game-time[^"']*["'][^>]*>([\s\S]*?)<\//i) || [])[1]); + const gameType = stripHtml((item.match(/class=["'][^"']*game-type[^"']*["'][^>]*>([\s\S]*?)<\//i) || [])[1]); + + const teams = []; + const teamReg = /class=["'][^"']*team-name[^"']*["'][^>]*>([\s\S]*?)<\//gi; + let tm; + while ((tm = teamReg.exec(item)) !== null) teams.push(stripHtml(tm[1])); + + let title = [time, gameType, teams.length >= 2 ? teams[0] + ' vs ' + teams[1] : teams.join(' vs ')].filter(Boolean).join(' ').trim(); + if (!title || title === 'vs') title = stripHtml(item).replace(/直播中|观看|高清|视频直播/g, '').trim(); + if (!title) continue; + + const btnTextMatch = item.match(/]+href=["'][^"']*\/live\/\d+\/play[^"']*["'][^>]*>([\s\S]*?)<\/a>/i); + const remark = stripHtml(btnTextMatch ? btnTextMatch[1] : '') || '直播中'; + if (onlyLiving && remark !== '直播中') continue; + const picMatch = item.match(/]+(?:data-src|src)=["']([^"']+)["']/i); + + list.push({ + vod_id: absUrl(hrefMatch[1]) + '###' + encodeURIComponent(title), + vod_name: title, + vod_pic: absUrl(picMatch ? picMatch[1] : '/static/img/default.png'), + vod_remarks: remark + }); + } + return list; +} + +async function fetchGroupList(group) { + let paths = ['/']; + if (group === 'basketball') { + paths = ['/match/1/live', '/match/2/live', '/match/20/live', '/match/4/live']; + } else if (group === 'football') { + paths = ['/match/3/live', '/match/8/live', '/match/9/live', '/match/10/live', '/match/14/live', '/match/15/live', '/match/12/live', '/match/13/live', '/match/7/live', '/match/11/live', '/match/27/live', '/match/26/live', '/match/31/live', '/match/23/live']; + } else if (group === 'other') { + paths = ['/match/21/live', '/match/29/live', '/match/25/live', '/match/19/live', '/match/38/live']; + } + + const all = []; + const seen = {}; + for (let i = 0; i < paths.length; i++) { + try { + const html = await fetchText(host + paths[i], host + '/'); + const items = parseList(html); + for (let j = 0; j < items.length; j++) { + const key = items[j].vod_id; + if (seen[key]) continue; + seen[key] = true; + all.push(items[j]); + } + } catch (e) {} + } + return all; +} + +async function init(cfg) { + if (cfg && cfg.ext && String(cfg.ext).indexOf('http') === 0) host = String(cfg.ext).trim().replace(/\/$/, ''); +} + +async function home(filter) { + return JSON.stringify({ class: getClasses(), filters: {} }); +} + +async function homeVod() { + return await category('0', 1, false, {}); +} + +async function category(tid, pg, filter, extend) { + tid = String((extend && extend.cateId) || tid || '0'); + pg = parseInt(pg) || 1; + let group = ''; + if (tid === '1' || tid === 'basketball') group = 'basketball'; + else if (tid === '8' || tid === 'football') group = 'football'; + else if (tid === '21' || tid === 'other') group = 'other'; + + let list = []; + try { + const onlyLiving = tid === 'live'; + list = group ? await fetchGroupList(group) : parseList(await fetchText(host + '/', host + '/'), onlyLiving); + } catch (e) { + list = []; + } + return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: 1, limit: 20, total: list.length, list }); +} + +async function detail(id) { + id = Array.isArray(id) ? id[0] : id; + let realId = String(id || ''); + let displayName = '赛事直播'; + if (realId.indexOf('###') >= 0) { + const parts = realId.split('###'); + realId = parts[0]; + try { displayName = decodeURIComponent(parts[1] || '赛事直播'); } catch (e) { displayName = parts[1] || '赛事直播'; } + } + + const match = realId.match(/\/live\/(\d+)\/play/); + const gameId = match ? match[1] : ''; + if (!gameId) return JSON.stringify({ code: 1, page: 1, pagecount: 1, limit: 0, total: 0, list: [] }); + + let playUrls = ''; + try { + const content = await fetchText(host + '/live/' + gameId + '/source', realId); + const json = safeJson(content, {}); + const data = decodePlaySource(json.data || ''); + const links = (data && data.links) || []; + const arr = []; + for (let i = 0; i < links.length; i++) { + const u = toDirectPlayUrl(links[i].url || ''); + if (u) arr.push((links[i].name || ('线路' + (i + 1))) + '$' + u); + } + playUrls = arr.join('#'); + } catch (e) { + playUrls = ''; + } + + return JSON.stringify({ + code: 1, + msg: '数据列表', + page: 1, + pagecount: 1, + limit: 1, + total: 1, + list: [{ + vod_id: realId, + vod_name: displayName, + vod_pic: host + '/static/img/default.png', + vod_remarks: '直播中', + vod_play_from: '88看球', + vod_play_url: playUrls, + vod_content: '实时体育直播' + }] + }); +} + +async function search(wd, quick, pg) { + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: 20, total: 0, list: [] }); +} + +async function play(flag, id, flags) { + const isDirect = /\.(m3u8|mp4)(\?|$)/i.test(String(id || '')); + return JSON.stringify({ parse: isDirect ? 0 : 1, url: id, header: headers }); +} + +// OK影视/部分 CAT 壳兼容入口。 +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 1, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + + +const __sports_src_1 = (function () { + + +let host = 'https://kafeizhibo.cc'; +const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'; +const headers = { + 'User-Agent': UA, + 'Referer': host + '/pc', + 'Accept': 'application/json, text/plain, */*' +}; + +function safeJson(text, def) { + try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } +} + +function absUrl(url) { + url = String(url || '').trim(); + if (!url) return ''; + if (/^https?:\/\//i.test(url)) return url; + if (url.indexOf('//') === 0) return 'https:' + url; + if (url.charAt(0) === '/') return host + url; + return host + '/' + url; +} + +function clean(s) { + return String(s || '').replace(/<[^>]+>/g, '').replace(/\s+/g, ' ').trim(); +} + +async function fetchJson(url) { + const r = await req(url, { headers }); + return safeJson((r && (r.content || r.body)) || '{}', {}); +} + +function getClasses() { + return [ + { type_id: 'all', type_name: '全部直播' }, + { type_id: 'hot', type_name: '热门直播' }, + { type_id: 'nba', type_name: 'NBA' }, + { type_id: '1', type_name: '足球直播' }, + { type_id: '2', type_name: '篮球直播' }, + { type_id: '3', type_name: '网球直播' }, + { type_id: '19', type_name: '台球直播' }, + { type_id: 'schedule', type_name: '赛程列表' }, + { type_id: 'recordings', type_name: '录像' } + ]; +} + +function titleOf(it) { + const mi = it.match_info || {}; + const league = it.league_name || mi.league_name || it.league || ''; + const home = it.home_team || mi.home_team || (it.homeTeam && it.homeTeam.name) || ''; + const away = it.away_team || mi.away_team || (it.awayTeam && it.awayTeam.name) || ''; + const title = it.title || it.name || ''; + if (title && title !== it.name) return clean(title); + if (league && home && away) return clean(league + ' ' + home + ' vs ' + away); + return clean(title || it.name || ('直播间 ' + (it.room_id || it.id || ''))); +} + +function remarkOf(it) { + const parts = []; + const status = it.status || (it.match_info && it.match_info.status) || ''; + if (status === 'live' || it.is_live) parts.push('直播中'); + else if (status === 'online') parts.push('在线'); + else if (status === 'upcoming') parts.push('未开赛'); + else if (status) parts.push(status); + const score = (it.home_score !== undefined && it.away_score !== undefined) ? (it.home_score + '-' + it.away_score) : ''; + if (score && score !== '0-0') parts.push(score); + if (it.heat) parts.push('热度:' + it.heat); + if (it.name && it.title && it.name !== it.title) parts.push(it.name); + return clean(parts.join(' ')) || '直播'; +} + +function picOf(it) { + return absUrl(it.screenshot || it.avatar || it.home_team_logo || it.away_team_logo || (it.homeTeam && it.homeTeam.logo) || (it.awayTeam && it.awayTeam.logo) || '/images/logo.png'); +} + +function itemToVod(it) { + const roomId = it.room_id || (it.archor && it.archor.room_id) || (Array.isArray(it.archors) && it.archors[0] && it.archors[0].room_id) || ''; + if (!roomId) return null; + return { + vod_id: String(roomId) + '###' + encodeURIComponent(titleOf(it)), + vod_name: titleOf(it), + vod_pic: picOf(it), + vod_remarks: remarkOf(it) + }; +} + +function recordingToVod(it) { + const matchId = it.match_id || it.id || ''; + if (!matchId) return null; + const name = titleOf(it); + return { + vod_id: 'rec$' + String(matchId) + '###' + encodeURIComponent(name), + vod_name: name, + vod_pic: absUrl(it.cover_image || it.screenshot || it.home_team_logo || it.away_team_logo || '/images/logo.png'), + vod_remarks: clean([it.start_time || '', it.recording_count ? ('录像:' + it.recording_count) : '录像'].filter(Boolean).join(' ')) + }; +} + +async function init(cfg) { + if (cfg && cfg.ext && String(cfg.ext).indexOf('http') === 0) host = String(cfg.ext).trim().replace(/\/$/, ''); +} + +async function home(filter) { + return JSON.stringify({ class: getClasses(), filters: {} }); +} + +async function homeVod() { + return await category('all', 1, false, {}); +} + +async function category(tid, pg, filter, extend) { + tid = String((extend && extend.cateId) || tid || 'all'); + pg = parseInt(pg) || 1; + const size = 30; + let apiUrl = ''; + + if (tid === 'schedule') { + apiUrl = host + '/api/v1/schedule?type=all&page=' + pg + '&size=' + size + '&_t=' + Date.now(); + } else if (tid === 'recordings') { + apiUrl = host + '/api/v1/recordings?page=' + pg + '&size=' + size + '&_t=' + Date.now(); + } else if (tid === 'nba') { + // 官网没有单独 nba 参数;用篮球赛程聚合后按 NBA 关键字过滤,有 NBA 时显示 NBA,无 NBA 时为空不混入其他篮球。 + apiUrl = host + '/api/v1/schedule?type=2&page=' + pg + '&size=100&_t=' + Date.now(); + } else { + const type = tid === 'all' ? '' : tid; + apiUrl = host + '/api/v1/archor?type=' + encodeURIComponent(type) + '&_t=' + Date.now(); + } + + let list = []; + let total = 0; + try { + const json = await fetchJson(apiUrl); + const data = Array.isArray(json.data) ? json.data : []; + const seen = {}; + for (let i = 0; i < data.length; i++) { + if (tid === 'recordings') { + const vod = recordingToVod(data[i]); + if (vod && !seen[vod.vod_id]) { seen[vod.vod_id] = true; list.push(vod); } + } else if (tid === 'nba') { + const title = titleOf(data[i]); + if (!/NBA|美职篮|美国职业篮球/i.test(title)) continue; + if (Array.isArray(data[i].archors) && data[i].archors.length) { + for (let j = 0; j < data[i].archors.length; j++) { + const merged = Object.assign({}, data[i], data[i].archors[j], { + title, + screenshot: data[i].screenshot || data[i].archors[j].screenshot + }); + const vod = itemToVod(merged); + if (vod && !seen[vod.vod_id]) { seen[vod.vod_id] = true; list.push(vod); } + } + } else { + const vod = itemToVod(data[i]); + if (vod && !seen[vod.vod_id]) { seen[vod.vod_id] = true; list.push(vod); } + } + } else if (tid === 'schedule' && Array.isArray(data[i].archors) && data[i].archors.length) { + for (let j = 0; j < data[i].archors.length; j++) { + const merged = Object.assign({}, data[i], data[i].archors[j], { + title: titleOf(data[i]), + screenshot: data[i].screenshot || data[i].archors[j].screenshot + }); + const vod = itemToVod(merged); + if (vod && !seen[vod.vod_id]) { seen[vod.vod_id] = true; list.push(vod); } + } + } else { + const vod = itemToVod(data[i]); + if (vod && !seen[vod.vod_id]) { seen[vod.vod_id] = true; list.push(vod); } + } + } + total = tid === 'nba' ? list.length : (json.total || list.length); + } catch (e) { + list = []; + } + return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: 1, limit: size, total, list }); +} + +async function detail(id) { + id = Array.isArray(id) ? id[0] : id; + let roomId = String(id || ''); + let displayName = '咖啡直播'; + if (roomId.indexOf('###') >= 0) { + const parts = roomId.split('###'); + roomId = parts[0]; + try { displayName = decodeURIComponent(parts[1] || displayName); } catch (e) { displayName = parts[1] || displayName; } + } + if (roomId.indexOf('rec$') === 0) { + const matchId = roomId.slice(4); + let vod = { + vod_id: roomId, + vod_name: displayName, + vod_pic: host + '/images/logo.png', + vod_remarks: '录像', + vod_play_from: '咖啡录像', + vod_play_url: '', + vod_content: '咖啡直播赛事录像' + }; + try { + const json = await fetchJson(host + '/api/v1/match/' + encodeURIComponent(matchId) + '/recordings?_t=' + Date.now()); + const data = json.data || {}; + const match = data.match || {}; + const urls = []; + const replays = Array.isArray(data.replays) ? data.replays : []; + const highlights = Array.isArray(data.highlights) ? data.highlights : []; + for (let i = 0; i < replays.length; i++) { + if (replays[i].video_url) urls.push(clean(replays[i].title || ('录像' + (i + 1))) + '$' + replays[i].video_url); + } + for (let i = 0; i < highlights.length; i++) { + if (highlights[i].video_url) urls.push(clean(highlights[i].title || ('集锦' + (i + 1))) + '$' + highlights[i].video_url); + } + vod = { + vod_id: roomId, + vod_name: titleOf(match) || displayName, + vod_pic: absUrl((replays[0] && replays[0].cover_image) || match.cover_image || match.home_team_logo || match.away_team_logo || '/images/logo.png'), + vod_remarks: clean([match.start_time || '', match.home_score !== undefined ? (match.home_score + '-' + match.away_score) : ''].filter(Boolean).join(' ')) || '录像', + vod_play_from: '咖啡录像', + vod_play_url: urls.join('#'), + vod_content: '咖啡直播赛事录像' + }; + } catch (e) {} + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: 1, total: 1, list: [vod] }); + } + if (!roomId) return JSON.stringify({ code: 1, page: 1, pagecount: 1, limit: 0, total: 0, list: [] }); + + let vod = { + vod_id: roomId, + vod_name: displayName, + vod_pic: host + '/images/logo.png', + vod_remarks: '直播', + vod_play_from: '咖啡直播', + vod_play_url: '', + vod_content: '咖啡直播实时体育直播' + }; + + try { + const json = await fetchJson(host + '/api/v1/room/' + encodeURIComponent(roomId) + '?_t=' + Date.now()); + const data = json.data || {}; + const room = data.room_info || {}; + const archor = data.archor || {}; + const signals = Array.isArray(data.signals) ? data.signals : []; + const urls = []; + const seen = {}; + + function addLine(name, url) { + url = String(url || '').trim(); + if (!url || seen[url]) return; + seen[url] = true; + urls.push(clean(name || ('线路' + (urls.length + 1))) + '$' + url); + } + + for (let i = 0; i < signals.length; i++) addLine(signals[i].name, signals[i].stream_url); + addLine(archor.name, archor.stream_url); + + const title = room.title || displayName; + vod = { + vod_id: roomId, + vod_name: clean(title), + vod_pic: absUrl(archor.screenshot || room.avatar || archor.avatar || '/images/logo.png'), + vod_remarks: remarkOf(Object.assign({}, room, archor)), + vod_play_from: '咖啡直播', + vod_play_url: urls.join('#'), + vod_content: clean(room.notice || room.notice_h5 || '咖啡直播实时体育直播') + }; + } catch (e) {} + + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: 1, total: 1, list: [vod] }); +} + +async function search(wd, quick, pg) { + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: 20, total: 0, list: [] }); +} + +async function play(flag, id, flags) { + return JSON.stringify({ parse: 0, url: id, header: headers }); +} + +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 0, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + + +const __sports_src_2 = (function () { + + +const API_BASE = 'https://01cs01.fusk39cd.com/api/web/live_lists'; +const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'; +const headers = { + 'User-Agent': UA, + 'Accept': 'application/json, text/plain, */*', + 'Referer': 'https://01cs01.fusk39cd.com/' +}; +const PAGE_SIZE = 20; + +function safeString(v) { + if (v === null || v === undefined || v === 'null') return ''; + return String(v); +} + +function safeJson(text, def) { + if (text && typeof text === 'object') return text; + try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } +} + +async function fetchJson(url) { + if (typeof Java !== 'undefined' && Java && Java.req) { + const r = Java.req(url, { headers }); + return safeJson((r && (r.body || r.content)) || '{}', {}); + } + const r2 = await req(url, { headers }); + return safeJson((r2 && (r2.content || r2.body)) || '{}', {}); +} + +function getClasses() { + return [ + { type_id: '1', type_name: '全部' }, + { type_id: '2', type_name: '足球' }, + { type_id: '3', type_name: '篮球' } + ]; +} + +async function init(cfg) {} + +async function home(filter) { + return JSON.stringify({ class: getClasses(), filters: {} }); +} + +async function homeVod() { + return await category('1', 1, false, {}); +} + +async function category(tid, pg, filter, extend) { + const page = Math.max(parseInt(pg || '1', 10) || 1, 1); + const typeId = safeString(tid || '1') || '1'; + const list = []; + + try { + const url = API_BASE + '/' + encodeURIComponent(typeId); + const json = await fetchJson(url); + const rows = json && json.code === 200 && json.data && Array.isArray(json.data.data) ? json.data.data : []; + + for (let i = 0; i < rows.length; i++) { + const item = rows[i]; + if (!item.tournament_id) continue; + const type = safeString(item.type || typeId); + const tournamentId = safeString(item.tournament_id); + const memberId = safeString(item.member_id); + const homeTeam = safeString(item.home_team_zh); + const awayTeam = safeString(item.away_team_zh); + const name = [homeTeam, awayTeam].filter(Boolean).join(' VS ') || safeString(item.league_name_zh) || tournamentId; + + list.push({ + vod_id: type + '|' + tournamentId + '|' + memberId, + vod_name: name, + vod_pic: safeString(item.cover), + vod_remarks: safeString(item.league_name_zh) || safeString(item.match_time) || safeString(item.status) + }); + } + } catch (e) { + return JSON.stringify({ code: 1, page, pagecount: 1, limit: PAGE_SIZE, total: 0, list: [] }); + } + + return JSON.stringify({ + code: 1, + msg: '数据列表', + page, + pagecount: list.length >= PAGE_SIZE ? page + 1 : page, + limit: PAGE_SIZE, + total: list.length, + list + }); +} + +async function detail(id) { + const idList = Array.isArray(id) ? id : String(id || '').split(',').filter(Boolean); + const list = []; + + for (let n = 0; n < idList.length; n++) { + const vid = safeString(idList[n]).trim(); + const parts = vid.split('|'); + if (parts.length !== 3) continue; + const type = parts[0]; + const tournamentId = parts[1]; + const memberId = parts[2]; + + try { + const url = API_BASE + '/' + encodeURIComponent(type) + '/detail/' + encodeURIComponent(tournamentId) + '?member_id=' + encodeURIComponent(memberId); + const json = await fetchJson(url); + if (!json || json.code !== 200) continue; + + const detailObj = json.data && json.data.detail ? json.data.detail : {}; + const moreArr = json.data && Array.isArray(json.data.more) ? json.data.more : []; + const homeTeam = safeString(detailObj.home_team_zh); + const awayTeam = safeString(detailObj.away_team_zh); + const vodName = [homeTeam, awayTeam].filter(Boolean).join(' VS ') || safeString(detailObj.league_name_zh) || vid; + + const playFromList = []; + const playUrlList = []; + for (let i = 0; i < moreArr.length; i++) { + const source = moreArr[i]; + const username = safeString(source.username) || ('线路' + (i + 1)); + const urls = []; + const flv = safeString(source.screen_url); + const m3u8 = safeString(source.screen_url_m3u8); + if (flv) urls.push('线路一$' + flv); + if (m3u8) urls.push('线路二$' + m3u8); + if (!urls.length) continue; + playFromList.push(username.replace(/\$|#/g, '')); + playUrlList.push(urls.join('#')); + } + + list.push({ + vod_id: vid, + vod_name: vodName, + vod_pic: safeString(detailObj.cover), + type_name: safeString(detailObj.league_name_zh), + vod_remarks: safeString(detailObj.match_time) || safeString(detailObj.status), + vod_content: safeString(detailObj.room_notice) || '体育直播', + vod_play_from: playFromList.join('$$$') || '919体育', + vod_play_url: playUrlList.join('$$$') || ('暂无播放$' + vid) + }); + } catch (e) {} + } + + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: list.length, total: list.length, list }); +} + +async function search(wd, quick, pg) { + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: 20, total: 0, list: [] }); +} + +async function play(flag, id, flags) { + const url = safeString(id).replace(/\*\*\*/g, '#'); + if (!/^https?:\/\//i.test(url)) return JSON.stringify({ parse: 0, jx: 0, url: '', msg: '暂无播放地址' }); + return JSON.stringify({ parse: 0, jx: 0, url, header: headers }); +} + +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 0, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + + +const __sports_src_3 = (function () { + + +let host = 'https://m.jrskk.com'; +const hosts = ['https://m.jrskk.com', 'https://m.jrs21.com', 'https://www.jrs33.com', 'https://3.swjrzx.com']; +const UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1'; +const defaultPic = 'https://im-imgs-bucket.oss-accelerate.aliyuncs.com/icon-192.png'; +let cacheTime = 0; +let cacheHtml = ''; + +const B64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +function stripHtml(s) { + return String(s || '') + .replace(//gi, '') + .replace(//gi, '') + .replace(/<[^>]+>/g, '') + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, ' ') + .trim(); +} + +function cleanText(s) { return String(s || '').replace(/\s+/g, ' ').trim(); } + +function absUrl(url, base) { + url = String(url || '').trim(); + base = base || host; + if (!url) return ''; + if (/^https?:\/\//i.test(url)) return url; + if (url.indexOf('//') === 0) return 'https:' + url; + if (url.charAt(0) === '/') return base + url; + return base + '/' + url; +} + +function utf8ToBase64Url(str) { + str = unescape(encodeURIComponent(String(str || ''))); + let out = ''; + for (let i = 0; i < str.length; i += 3) { + const c1 = str.charCodeAt(i); + const c2 = str.charCodeAt(i + 1); + const c3 = str.charCodeAt(i + 2); + out += B64_CHARS.charAt(c1 >> 2); + out += B64_CHARS.charAt(((c1 & 3) << 4) | ((c2 || 0) >> 4)); + out += isNaN(c2) ? '=' : B64_CHARS.charAt(((c2 & 15) << 2) | ((c3 || 0) >> 6)); + out += isNaN(c3) ? '=' : B64_CHARS.charAt(c3 & 63); + } + return out.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/g, ''); +} + +function base64UrlToUtf8(str) { + str = String(str || '').replace(/-/g, '+').replace(/_/g, '/'); + while (str.length % 4) str += '='; + let bytes = []; + for (let i = 0; i < str.length; i += 4) { + const c1 = B64_CHARS.indexOf(str.charAt(i)); + const c2 = B64_CHARS.indexOf(str.charAt(i + 1)); + const c3 = B64_CHARS.indexOf(str.charAt(i + 2)); + const c4 = B64_CHARS.indexOf(str.charAt(i + 3)); + if (c1 < 0 || c2 < 0) continue; + const n = (c1 << 18) | (c2 << 12) | ((c3 < 0 ? 0 : c3) << 6) | (c4 < 0 ? 0 : c4); + bytes.push((n >> 16) & 255); + if (str.charAt(i + 2) !== '=') bytes.push((n >> 8) & 255); + if (str.charAt(i + 3) !== '=') bytes.push(n & 255); + } + let raw = ''; + for (let j = 0; j < bytes.length; j++) raw += String.fromCharCode(bytes[j]); + try { return decodeURIComponent(escape(raw)); } catch (e) { return raw; } +} + +function safeJson(text, def) { try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } } + +function matchCategory(tid, league, name, stype, hot) { + tid = String(tid || 'all'); + const text = league + ' ' + name; + if (tid === 'all' || tid === 'live') return true; + if (tid === 'hot') return !!hot || /(NBA|CBA|英超|西甲|意甲|德甲|法甲|欧冠|中超|世界杯|世俱杯|亚冠|热门)/i.test(text); + if (tid === 'basketball') return stype === 'lq' || /(NBA|CBA|WNBA|NBL|篮球|篮)/i.test(text); + if (tid === 'football') return stype === 'zq' || (/(足球|英超|西甲|意甲|德甲|法甲|欧冠|欧联|中超|亚冠|足协|世界杯|世俱|巴西甲|巴西乙|日职|韩K|联赛|杯)/i.test(text) && !/(NBA|CBA|WNBA|NBL|篮球|篮)/i.test(text)); + if (tid === 'other') return !matchCategory('basketball', league, name, stype, hot) && !matchCategory('football', league, name, stype, hot); + return true; +} + +async function fetchText(url, referer) { + const hd = { 'User-Agent': UA, 'Referer': referer || host + '/', 'Accept': 'text/html,application/json,*/*', 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8' }; + if (typeof Java !== 'undefined' && Java && Java.req) { + const r = await Java.req(url, { headers: hd }); + if (typeof r === 'string') return r; + return String((r && (r.body || r.content || r.data)) || ''); + } + const r2 = await req(url, { headers: hd }); + return String((r2 && (r2.content || r2.body)) || ''); +} + +async function fetchHome(force) { + if (!force && cacheHtml && Date.now() - cacheTime < 60000) return cacheHtml; + for (let i = 0; i < hosts.length; i++) { + try { + const html = await fetchText(hosts[i] + '/', hosts[i] + '/'); + if (html && (/loc_match|lab_team|JRKAN|play\/steam/i).test(html)) { + host = hosts[i]; cacheHtml = html; cacheTime = Date.now(); return html; + } + } catch (e) {} + } + return cacheHtml || ''; +} + +function firstMatch(text, reg) { const m = String(text || '').match(reg); return m ? m[1] : ''; } + +function parseList(html, tid) { + const list = []; + const reg = /]*class=["'][^"']*item[^"']*["'][^>]*>[\s\S]*?<\/ul>/gi; + let m; + while ((m = reg.exec(String(html || ''))) !== null) { + const item = m[0]; + if (!/class=["'][^"']*ok[^"']*me[^"']*["']/i.test(item)) continue; + const links = []; + const areg = /]*class=["'][^"']*ok[^"']*me[^"']*["'][^>]*)>([\s\S]*?)<\/a>/gi; + let am; + while ((am = areg.exec(item)) !== null) { + const attrs = am[1]; + const href = firstMatch(attrs, /href=["']([^"']+)["']/i) || firstMatch(attrs, /data-play=["']([^"']+)["']/i); + if (!href || href === 'javascript:void(0)') continue; + const name = stripHtml(am[2]) || ('线路' + (links.length + 1)); + links.push({ name, url: absUrl(href, host) }); + } + if (!links.length) continue; + const league = stripHtml(firstMatch(item, /class=["'][^"']*lab_events[^"']*["'][\s\S]*?]*class=["']name["'][^>]*>([\s\S]*?)<\/span>/i)); + const time = stripHtml(firstMatch(item, /class=["'][^"']*lab_time[^"']*["'][^>]*>([\s\S]*?)<\/li>/i)); + const home = stripHtml(firstMatch(item, /class=["'][^"']*lab_team_home[^"']*["'][\s\S]*?]*class=["']name["'][^>]*>([\s\S]*?)<\/strong>/i)); + const away = stripHtml(firstMatch(item, /class=["'][^"']*lab_team_away[^"']*["'][\s\S]*?]*class=["']name["'][^>]*>([\s\S]*?)<\/strong>/i)); + const stype = firstMatch(item, /data-stype=["']([^"']+)["']/i); + const hot = /class=["'][^"']*hot[^"']*["']/i.test(item); + let name = [time, league, [home, away].filter(Boolean).join(' vs ')].filter(Boolean).join(' '); + if (!name) name = stripHtml(item).slice(0, 80) || '赛事直播'; + if (!matchCategory(tid, league, name, stype, hot)) continue; + const pic = absUrl(firstMatch(item, /]+src=["']([^"']+)["']/i) || defaultPic, host); + const payload = { name, pic, links }; + list.push({ vod_id: 'jrs$' + utf8ToBase64Url(JSON.stringify(payload)), vod_name: name, vod_pic: pic, vod_remarks: links.map(x => x.name).slice(0, 3).join('/') || '直播' }); + } + return list; +} + +function getClasses() { + return [ + { type_id: 'live', type_name: '正在直播' }, + { type_id: 'all', type_name: '全部赛程' }, + { type_id: 'basketball', type_name: '篮球直播' }, + { type_id: 'football', type_name: '足球直播' }, + { type_id: 'hot', type_name: '热门赛事' }, + { type_id: 'other', type_name: '其他赛事' } + ]; +} + +async function init(cfg) { if (cfg && cfg.ext && String(cfg.ext).indexOf('http') === 0) host = String(cfg.ext).replace(/\/$/, ''); } +async function home(filter) { return JSON.stringify({ class: getClasses(), filters: {} }); } +async function homeVod() { return await category('live', 1, false, {}); } +async function category(tid, pg, filter, extend) { + const html = await fetchHome(false); + const list = parseList(html, tid || 'all'); + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: list.length, total: list.length, list }); +} +async function detail(id) { + id = Array.isArray(id) ? id[0] : id; + let payload = null; + id = String(id || ''); + if (id.indexOf('jrs$') === 0) payload = safeJson(base64UrlToUtf8(id.slice(4)), null); + if (!payload && /^https?:\/\//i.test(id)) payload = { name: '赛事直播', pic: defaultPic, links: [{ name: '直播', url: id }] }; + if (!payload) return JSON.stringify({ code: 1, list: [], page: 1, pagecount: 1, total: 0 }); + const playUrl = (payload.links || []).map((x, i) => (x.name || ('线路' + (i + 1))) + '$' + x.url).join('#'); + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: 1, total: 1, list: [{ vod_id: id, vod_name: payload.name || '赛事直播', vod_pic: payload.pic || defaultPic, vod_remarks: '直播', vod_content: 'JRKAN 体育赛事直播。部分线路为网页播放器,播放器可嗅探播放。', vod_play_from: 'JRS直播', vod_play_url: playUrl }] }); +} +async function search(wd, quick, pg) { return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: 20, total: 0, list: [] }); } +async function play(flag, id, flags) { return JSON.stringify({ parse: /\.(m3u8|mp4|flv)(\?|$)/i.test(String(id || '')) ? 0 : 1, url: id, header: { 'User-Agent': UA, 'Referer': host + '/' } }); } + +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 1, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + + +const __sports_src_4 = (function () { + + +let host = 'https://www.iqzhibo.com'; +const dbHost = 'https://www.doubaozhibo.com'; +const dbScheduleApi = dbHost + '/api/v1/schedules/public/local'; +const dbPlaybackApi = dbHost + '/api/v1/playbacks'; +const UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36'; + +function getHeaders(referer) { + return { + 'User-Agent': UA, + 'Referer': referer || host + '/', + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'zh-CN,zh;q=0.9,en;q=0.8' + }; +} + +function getHeaderValue(headers, name) { + if (!headers) return ''; + const lower = String(name || '').toLowerCase(); + for (const k in headers) { + if (String(k).toLowerCase() === lower) { + const v = headers[k]; + return Array.isArray(v) ? String(v[0] || '') : String(v || ''); + } + } + return ''; +} + +function stripHtml(s) { + return String(s || '') + .replace(//gi, '') + .replace(//gi, '') + .replace(/<[^>]+>/g, '') + .replace(/ /g, ' ') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/'/g, "'") + .replace(/\s+/g, ' ') + .trim(); +} + +function cleanText(s) { + return stripHtml(s).replace(/\s+/g, ' ').trim(); +} + +function absUrl(url, base) { + url = String(url || '').trim(); + base = base || host; + if (!url) return ''; + if (/^https?:\/\//i.test(url)) return url; + if (url.indexOf('//') === 0) return 'https:' + url; + if (url.charAt(0) === '/') return base.replace(/\/$/, '') + url; + return base.replace(/\/$/, '') + '/' + url; +} + +function safeJson(text, def) { + try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } +} + +async function fetchText(url, referer) { + const hd = getHeaders(referer || host + '/'); + if (typeof Java !== 'undefined' && Java && Java.req) { + const r = await Java.req(url, { headers: hd }); + if (typeof r === 'string') return r; + const code = Number((r && (r.statusCode || r.status || r.code)) || 0); + const loc = getHeaderValue(r && r.headers, 'location'); + if (loc && code >= 300 && code < 400) return await fetchText(absUrl(loc, url.indexOf(dbHost) === 0 ? dbHost : host), url); + return String((r && (r.body || r.content || r.data)) || ''); + } + const r2 = await req(url, { headers: hd }); + if (typeof r2 === 'string') return r2; + const code2 = Number((r2 && (r2.statusCode || r2.status || r2.code)) || 0); + const loc2 = getHeaderValue(r2 && r2.headers, 'location'); + if (loc2 && code2 >= 300 && code2 < 400) return await fetchText(absUrl(loc2, url.indexOf(dbHost) === 0 ? dbHost : host), url); + return String((r2 && (r2.content || r2.body || r2.data)) || ''); +} + +function getClasses() { + return [ + { type_id: 'zuqiu', type_name: '足球直播' }, + { type_id: 'lanqiu', type_name: '篮球直播' }, + { type_id: 'sssc', type_name: '实时赛程' }, + { type_id: 'huifang', type_name: '赛事回放' }, + { type_id: 'fifa', type_name: '世界杯' }, + { type_id: 'zhongchao', type_name: '中超' }, + { type_id: 'yingchao', type_name: '英超' }, + { type_id: 'xijia', type_name: '西甲' }, + { type_id: 'dejia', type_name: '德甲' }, + { type_id: 'yijia', type_name: '意甲' }, + { type_id: 'fajia', type_name: '法甲' } + ]; +} + +function parseIqList(html) { + html = String(html || ''); + const list = []; + const reg = /
  • \s*
    /gi; + let m; + while ((m = reg.exec(html)) !== null) { + const item = m[0]; + const title = cleanText((item.match(/class=["']title["'][^>]*>([\s\S]*?)<\/div>/i) || [])[1]); + const homeBlock = (item.match(/class=["']team-zhu["'][^>]*>([\s\S]*?)<\/div>/i) || [])[1] || ''; + const awayBlock = (item.match(/class=["']team-ke["'][^>]*>([\s\S]*?)<\/div>/i) || [])[1] || ''; + const home = cleanText(homeBlock.replace(/]*>/i, '')); + const hrefMatch = item.match(/
    ]+href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/i); + if (!hrefMatch) continue; + const imgMatch = item.match(/]+src=["']([^"']+)["']/i); + const teams = [home, away].filter(Boolean).join(' vs '); + const name = [title, teams].filter(Boolean).join(' '); + list.push({ + vod_id: absUrl(hrefMatch[1], host), + vod_name: name || teams || title || '赛事直播', + vod_pic: absUrl(imgMatch ? imgMatch[1] : '/logo.png', host), + vod_remarks: cleanText(hrefMatch[2]) || '高清' + }); + } + return list; +} + +function matchCategory(tid, league, name, dataType) { + tid = String(tid || ''); + league = cleanText(league); + name = cleanText(name); + dataType = String(dataType || ''); + const text = league + ' ' + name; + if (!tid || tid === 'sssc' || tid === 'huifang') return true; + if (tid === 'zuqiu') return dataType === 'football' || (/(足|超|甲|乙|冠|欧联|日职|韩K|世界杯|世俱|足球|杯)/i.test(text) && !/(篮|NBA|CBA|WNBA|NBL|篮球)/i.test(text)); + if (tid === 'lanqiu') return dataType === 'basketball' || /(篮|NBA|CBA|WNBA|NBL|篮球)/i.test(text); + const rules = { + fifa: /(世界杯|世俱杯|世预赛|国际足联|FIFA)/i, + zhongchao: /(中超|足协杯|中国超级联赛)/i, + yingchao: /(英超|英格兰超级联赛)/i, + xijia: /(西甲|西班牙甲级联赛)/i, + dejia: /(德甲|德国甲级联赛)/i, + yijia: /(意甲|意大利甲级联赛)/i, + fajia: /(法甲|法国甲级联赛)/i + }; + return rules[tid] ? rules[tid].test(text) : true; +} + +function formatTimeText(matchTime) { + const d = new Date(matchTime); + if (!matchTime || isNaN(d.getTime())) return ''; + const pad = function (n) { return String(n).padStart(2, '0'); }; + return pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + pad(d.getHours()) + ':' + pad(d.getMinutes()); +} + +async function fetchJson(url, referer) { + const text = await fetchText(url, referer || dbHost + '/'); + try { return JSON.parse(text || '{}'); } catch (e) { return {}; } +} + +function parseApiSchedule(json, tid) { + const days = json && json.data && Array.isArray(json.data.days) ? json.data.days : []; + const list = []; + for (let i = 0; i < days.length; i++) { + const rows = Array.isArray(days[i].live) ? days[i].live : []; + for (let j = 0; j < rows.length; j++) { + const item = rows[j]; + const signals = Array.isArray(item.signals) ? item.signals : []; + if (!signals.length) continue; + const name = [formatTimeText(item.matchTime), cleanText(item.league), cleanText(item.teamA) + ' vs ' + cleanText(item.teamB)].filter(Boolean).join(' '); + if (!matchCategory(tid, item.league, name, item.dataType)) continue; + const links = signals.map(function (s, idx) { + return { name: cleanText(s.name || s.label) || ('信号' + (idx + 1)), url: dbHost + '/play/' + s.playId + '?device=pc_web' }; + }); + list.push({ + vod_id: 'db$' + encodeURIComponent(JSON.stringify({ name: name, links: links })), + vod_name: name, + vod_pic: item.teamAImage || item.teamBImage || host + '/logo.png', + vod_remarks: links.map(function (x) { return x.name; }).join('/') || '直播' + }); + } + } + return list; +} + +function parsePlaybackList(json, tid) { + const rows = json && json.data && Array.isArray(json.data.list) ? json.data.list : []; + const list = []; + for (let i = 0; i < rows.length; i++) { + const s = rows[i].schedule || {}; + const p = rows[i].playback || {}; + const lines = Array.isArray(p.lines) ? p.lines : []; + if (!lines.length) continue; + const fullName = cleanText(p.title) || [formatTimeText(s.matchTime), cleanText(s.league), cleanText(s.teamA) + ' vs ' + cleanText(s.teamB), '回放'].filter(Boolean).join(' '); + if (!matchCategory(tid, s.league, fullName, s.dataType)) continue; + const shortName = (cleanText(s.teamA) + ' vs ' + cleanText(s.teamB)).replace(/^\s*vs\s*|\s*vs\s*$/g, '') || fullName.replace(/^\d{4}年\d{1,2}月\d{1,2}日\s*/, '').replace(/\s*赛事回放$/, ''); + const remarkPrefix = [formatTimeText(s.matchTime), cleanText(s.league)].filter(Boolean).join(' '); + const links = lines.map(function (line, idx) { + return { name: cleanText(line.title) || ('回放' + (idx + 1)), url: absUrl(line.proxyUrl, dbHost) }; + }); + list.push({ + vod_id: 'db$' + encodeURIComponent(JSON.stringify({ name: fullName, links: links })), + vod_name: shortName, + vod_pic: s.teamAImage || s.teamBImage || host + '/logo.png', + vod_remarks: [remarkPrefix, links.map(function (x) { return x.name; }).join('/')].filter(Boolean).join(' · ') || '回放' + }); + } + return list; +} + +function parseProxySchedule(html, tid) { + html = String(html || ''); + const list = []; + const reg = /]*class=["'][^"']*px-3 py-3 sm:px-4[^"']*["'][^>]*>[\s\S]*?<\/article>/gi; + let m; + while ((m = reg.exec(html)) !== null) { + const item = m[0]; + const ps = []; + const pReg = /]*>([\s\S]*?)<\/p>/gi; + let pm; + while ((pm = pReg.exec(item)) !== null) ps.push(cleanText(pm[1])); + const time = ps[0] || ''; + const league = ps[1] || ''; + + const teams = []; + const spanReg = /]*class=["'][^"']*truncate[^"']*["'][^>]*>([\s\S]*?)<\/span>/gi; + let sm; + while ((sm = spanReg.exec(item)) !== null) { + const t = cleanText(sm[1]); + if (t && !/^信号|高清|返回/.test(t) && teams.indexOf(t) < 0) teams.push(t); + } + if (teams.length < 2) continue; + const name = [time, league, teams[0] + ' vs ' + teams[1]].filter(Boolean).join(' '); + if (!matchCategory(tid, league, name)) continue; + + const links = []; + const aReg = /]+href=["']([^"']+)["'][^>]*class=["'][^"']*home-signal-link[^"']*["'][^>]*>([\s\S]*?)<\/a>/gi; + let am; + while ((am = aReg.exec(item)) !== null) { + const title = (am[0].match(/title=["']([^"']+)["']/i) || [])[1] || cleanText(am[2]) || ('信号' + (links.length + 1)); + links.push({ name: title, url: absUrl(am[1], dbHost) }); + } + if (!links.length) continue; + const img = (item.match(/]+src=["']([^"']+)["']/i) || [])[1] || '/logo.png'; + list.push({ + vod_id: 'db$' + encodeURIComponent(JSON.stringify({ name: name, links: links })), + vod_name: name, + vod_pic: absUrl(img, dbHost), + vod_remarks: links.map(function (x) { return x.name; }).join('/') || '直播' + }); + } + return list; +} + +async function init(cfg) { + if (cfg && cfg.ext && String(cfg.ext).indexOf('http') === 0) host = String(cfg.ext).trim().replace(/\/$/, ''); +} + +async function home(filter) { + return JSON.stringify({ class: getClasses(), filters: {} }); +} + +async function homeVod() { + return await category('zuqiu', 1, false, {}); +} + +async function category(tid, pg, filter, extend) { + tid = String((extend && extend.cateId) || tid || 'zuqiu'); + pg = parseInt(pg) || 1; + let list = []; + try { + if (tid === 'huifang') { + list = parsePlaybackList(await fetchJson(dbPlaybackApi + '?page=' + pg + '&pageSize=20&dataType=all', dbHost + '/'), tid); + } else { + list = parseApiSchedule(await fetchJson(dbScheduleApi, dbHost + '/'), tid); + if (!list.length && tid !== 'lanqiu') { + list = parseProxySchedule(await fetchText(host + '/proxy.php', host + '/sssc/'), tid); + } + } + } catch (e) { + list = []; + } + return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: list.length >= 20 ? pg + 1 : 1, limit: 50, total: list.length, list: list }); +} + +function parseNuxtProxy(html) { + html = String(html || ''); + const hls = html.match(/(?:\\u002F|\/)hls(?:\\u002F|\/)[A-Za-z0-9._-]+\.m3u8/i); + if (hls) return absUrl(hls[0].replace(/\\u002F/g, '/'), dbHost); + const m = html.match(/"proxyUrl"\s*:\s*"((?:\\.|[^"\\])*)"/); + if (m) { + try { return absUrl(JSON.parse('"' + m[1] + '"'), dbHost); } catch (e) { return absUrl(m[1].replace(/\\u002F/g, '/'), dbHost); } + } + const iframe = html.match(/https?:\/\/www\.kanqiuge\.com\/embed\/play\/[A-Za-z0-9._-]+/i); + if (iframe) return iframe[0]; + return ''; +} + +async function resolvePlayUrl(playUrl) { + playUrl = String(playUrl || ''); + if (/\.(m3u8|flv|mp4)(\?|$)/i.test(playUrl)) return playUrl; + try { + const html = await fetchText(playUrl, host + '/sssc/'); + return parseNuxtProxy(html) || playUrl; + } catch (e) { + return playUrl; + } +} + +async function detail(id) { + id = Array.isArray(id) ? id[0] : id; + id = String(id || ''); + if (id.indexOf('db$') === 0) { + let data = { name: '实时赛程', links: [] }; + try { data = JSON.parse(decodeURIComponent(id.slice(3))); } catch (e) {} + const playUrls = (data.links || []).map(function (x) { return (x.name || '线路') + '$' + x.url; }).join('#'); + return JSON.stringify({ + code: 1, + msg: '数据列表', + page: 1, + pagecount: 1, + limit: 1, + total: 1, + list: [{ + vod_id: id, + vod_name: data.name || '实时赛程', + vod_pic: host + '/logo.png', + vod_remarks: '直播', + vod_play_from: '爱球直播', + vod_play_url: playUrls, + vod_content: '爱球直播实时赛程,播放时解析豆包直播信号。' + }] + }); + } + + let name = '赛事直播'; + let time = ''; + let playUrls = ''; + try { + const html = await fetchText(id, host + '/'); + name = cleanText((html.match(/]*>([\s\S]*?)<\/h1>/i) || [])[1]) || name; + time = cleanText((html.match(/
    ]*>([\s\S]*?)<\/p>/i) || [])[1]); + const arr = []; + const aReg = /
    /i; + const block = (html.match(aReg) || [])[0] || ''; + const linkReg = /]+href=["']([^"']+)["'][^>]*>([\s\S]*?)<\/a>/gi; + let lm; + while ((lm = linkReg.exec(block)) !== null) { + const text = cleanText(lm[2]) || ('信号' + (arr.length + 1)); + if (/赛程|返回|首页/.test(text)) continue; + const href = lm[1]; + arr.push(text + '$' + (href === '/sssc/' || href.indexOf('/sssc/') >= 0 ? host + '/proxy.php' : absUrl(href, host))); + } + if (!arr.length) arr.push('赛程$' + host + '/proxy.php'); + playUrls = arr.join('#'); + } catch (e) { + playUrls = '赛程$' + host + '/proxy.php'; + } + + return JSON.stringify({ + code: 1, + msg: '数据列表', + page: 1, + pagecount: 1, + limit: 1, + total: 1, + list: [{ + vod_id: id, + vod_name: name, + vod_pic: host + '/logo.png', + vod_remarks: time, + vod_play_from: '爱球直播', + vod_play_url: playUrls, + vod_content: time || '爱球直播赛事导航' + }] + }); +} + +async function search(wd, quick, pg) { + pg = parseInt(pg) || 1; + let list = []; + try { + const html = await fetchText(host + '/search.html?keywords=' + encodeURIComponent(wd || '') + '&method=1', host + '/'); + list = parseIqList(html); + } catch (e) { + list = []; + } + return JSON.stringify({ code: 1, msg: '数据列表', page: pg, pagecount: 1, limit: 20, total: list.length, list: list }); +} + +async function play(flag, id, flags) { + const url = await resolvePlayUrl(id); + const direct = /\.(m3u8|flv|mp4)(\?|$)/i.test(String(url || '')); + return JSON.stringify({ + parse: direct ? 0 : 1, + url: url, + header: { + 'User-Agent': UA, + 'Referer': String(url || '').indexOf('doubaozhibo.com') >= 0 ? dbHost + '/' : host + '/' + } + }); +} + +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 1, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + + + +const __sports_src_5 = (function () { + + +const API_BASE = 'https://aapi2.xbncs.com/api'; +const HEADERS = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36', + 'Accept': 'application/json, text/plain, */*', + 'Referer': 'https://aapi2.xbncs.com/' +}; +const PAGE_SIZE = 30; + +function safeString(v) { if (v === null || v === undefined || v === 'null') return ''; return String(v); } +function safeJson(text, def) { if (text && typeof text === 'object') return text; try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } } + +async function requestJson(url) { + if (typeof Java !== 'undefined' && Java && Java.req) { + const r = await Java.req(url, { headers: HEADERS }); + if (typeof r === 'string') return safeJson(r, {}); + return safeJson((r && (r.body || r.content || r.data)) || '{}', {}); + } + const r2 = await req(url, { headers: HEADERS }); + return safeJson((r2 && (r2.content || r2.body || r2.data)) || '{}', {}); +} + +function getClasses() { return [{ type_id: '-1', type_name: '全部' }, { type_id: '1', type_name: '足球' }, { type_id: '2', type_name: '篮球' }]; } +async function init(cfg) {} +async function home(filter) { return JSON.stringify({ class: getClasses(), filters: {} }); } +async function homeVod() { return await category('-1', 1, false, {}); } + +async function category(tid, pg, filter, extend) { + const page = Math.max(parseInt(pg || '1', 10) || 1, 1); + const navId = tid === '-1' || tid === undefined || tid === null ? '' : String(tid); + const url = API_BASE + '/room/page?roomType=&navId=' + encodeURIComponent(navId) + '&roomId=&word=&page=' + page + '&pageSize=' + PAGE_SIZE + '&channelId=3&platform=1'; + const list = []; + try { + const json = await requestJson(url); + const rows = json && json.data && Array.isArray(json.data.list) ? json.data.list : []; + for (let i = 0; i < rows.length; i++) { + const item = rows[i]; + const roomId = safeString(item.roomId); + if (!roomId) continue; + list.push({ + vod_id: roomId, + vod_name: safeString(item.title) || safeString(item.nickName) || ('房间 ' + roomId), + vod_pic: safeString(item.cover), + vod_remarks: safeString(item.navName) || safeString(item.statusName) || safeString(item.liveStatus) || '直播' + }); + } + } catch (e) {} + return JSON.stringify({ code: 1, msg: '数据列表', page: page, pagecount: list.length >= PAGE_SIZE ? page + 1 : page, limit: PAGE_SIZE, total: list.length, list: list }); +} + +async function detail(id) { + id = Array.isArray(id) ? id[0] : id; + const roomId = safeString(id).trim(); + if (!roomId) return JSON.stringify({ code: 1, list: [], page: 1, pagecount: 1, total: 0 }); + const vod = { vod_id: roomId, vod_name: '房间 ' + roomId, vod_pic: '', vod_content: '体育直播', type_name: '', vod_play_from: '球通', vod_play_url: '' }; + try { + const json = await requestJson(API_BASE + '/room/info?roomId=' + encodeURIComponent(roomId) + '&channelId=3&platform=1'); + const data = json && json.data ? json.data : null; + if (data) { + vod.vod_name = safeString(data.title) || vod.vod_name; + vod.vod_pic = safeString(data.cover); + vod.vod_content = safeString(data.description) || safeString(data.notice) || '体育直播'; + vod.type_name = safeString(data.nickName) || safeString(data.navName); + const playUrls = []; + const pushUrl = safeString(data.pushUrl); + const pullUrl = safeString(data.pullUrl); + if (pushUrl) playUrls.push('flv$' + pushUrl); + if (pullUrl) playUrls.push('m3u8$' + pullUrl); + vod.vod_play_url = playUrls.join('#'); + } + } catch (e) {} + if (!vod.vod_play_url) vod.vod_play_url = '暂无播放$' + roomId; + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: 1, total: 1, list: [vod] }); +} + +async function search(wd, quick, pg) { return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: 0, total: 0, list: [] }); } +async function play(flag, id, flags) { id = safeString(id).replace(/\*\*\*/g, '#'); return JSON.stringify({ parse: /^https?:\/\//i.test(id) ? 0 : 1, jx: 0, url: /^https?:\/\//i.test(id) ? id : '', msg: /^https?:\/\//i.test(id) ? '' : '暂无播放地址', header: HEADERS }); } +async function homeContent(filter) { return safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return safeJson(await search(wd, quick, pg || 1), { list: [] }); } +async function playerContent(flag, id, flags) { return safeJson(await play(flag, id, flags), { parse: 1, url: id }); } + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +})(); + +const SOURCES = [ + { id: 's0', name: '88看球', api: __sports_src_0 }, + { id: 's1', name: '咖啡直播', api: __sports_src_1 }, + { id: 's2', name: '919体育', api: __sports_src_2 }, + { id: 's3', name: 'JRS直播', api: __sports_src_3 }, + { id: 's4', name: '爱球直播', api: __sports_src_4 }, + { id: 's5', name: '球通体育', api: __sports_src_5 } +]; + +const SOURCE_CATS = { + s0: [ + { n: '正在直播', v: 'live' }, + { n: '全部直播', v: '0' }, + { n: '篮球直播', v: '1' }, + { n: '足球直播', v: '8' }, + { n: '其他直播', v: '21' } + ], + s1: [ + { n: '全部直播', v: 'all' }, + { n: '热门直播', v: 'hot' }, + { n: 'NBA', v: 'nba' }, + { n: '足球直播', v: '1' }, + { n: '篮球直播', v: '2' }, + { n: '网球直播', v: '3' }, + { n: '台球直播', v: '19' }, + { n: '赛程列表', v: 'schedule' }, + { n: '录像', v: 'recordings' } + ], + s2: [ + { n: '全部', v: '1' }, + { n: '足球', v: '2' }, + { n: '篮球', v: '3' } + ], + s3: [ + { n: '正在直播', v: 'live' }, + { n: '全部赛程', v: 'all' }, + { n: '篮球直播', v: 'basketball' }, + { n: '足球直播', v: 'football' }, + { n: '热门赛事', v: 'hot' }, + { n: '其他赛事', v: 'other' } + ], + s4: [ + { n: '足球直播', v: 'zuqiu' }, + { n: '篮球直播', v: 'lanqiu' }, + { n: '实时赛程', v: 'sssc' }, + { n: '赛事回放', v: 'huifang' }, + { n: '世界杯', v: 'fifa' }, + { n: '中超', v: 'zhongchao' }, + { n: '英超', v: 'yingchao' }, + { n: '西甲', v: 'xijia' }, + { n: '德甲', v: 'dejia' }, + { n: '意甲', v: 'yijia' }, + { n: '法甲', v: 'fajia' } + ], + s5: [ + { n: '全部', v: '-1' }, + { n: '足球', v: '1' }, + { n: '篮球', v: '2' } + ] +}; + +function __safeJson(text, def) { + if (text && typeof text === 'object') return text; + try { return JSON.parse(text || '{}'); } catch (e) { return def || {}; } +} + +function __packId(srcId, id) { + return srcId + '@@@' + String(id || ''); +} + +function __splitId(id) { + const s = String(Array.isArray(id) ? id[0] : (id || '')); + const p = s.indexOf('@@@'); + if (p < 0) return { srcId: '', raw: s }; + return { srcId: s.slice(0, p), raw: s.slice(p + 3) }; +} + +function __srcById(srcId) { + for (let i = 0; i < SOURCES.length; i++) if (SOURCES[i].id === srcId) return SOURCES[i]; + return null; +} + +function __firstCat(srcId) { + const arr = SOURCE_CATS[srcId] || []; + return arr.length ? arr[0].v : 'home'; +} + +function __classes() { + const arr = []; + for (let i = 0; i < SOURCES.length; i++) { + arr.push({ type_id: SOURCES[i].id, type_name: SOURCES[i].name }); + } + return arr; +} + +function __filters() { + const filters = {}; + for (let i = 0; i < SOURCES.length; i++) { + const src = SOURCES[i]; + const values = SOURCE_CATS[src.id] || []; + filters[src.id] = [{ key: 'cat', name: '分类', value: values }]; + } + return filters; +} + +function __pickExtendCat(extend) { + extend = extend || {}; + return String(extend.cat || extend.type || extend.class || extend.cate || ''); +} + +function __prefixList(list, src) { + list = Array.isArray(list) ? list : []; + const out = []; + for (let i = 0; i < list.length; i++) { + const it = list[i] || {}; + const id = String(it.vod_id || ''); + if (!id) continue; + out.push(Object.assign({}, it, { + vod_id: __packId(src.id, id), + vod_name: String(it.vod_name || ''), + vod_remarks: it.vod_remarks || src.name + })); + } + return out; +} + +async function __callList(src, subTid, pg, filter, extend) { + try { + subTid = subTid || __firstCat(src.id); + const obj = __safeJson(await src.api.category(subTid, pg || 1, filter || false, extend || {}), {}); + return __prefixList(obj.list || [], src); + } catch (e) { + return []; + } +} + +async function init(cfg) { + for (let i = 0; i < SOURCES.length; i++) { + try { if (SOURCES[i].api.init) await SOURCES[i].api.init(cfg || {}); } catch (e) {} + } +} + +async function home(filter) { + return JSON.stringify({ class: __classes(), filters: __filters() }); +} + +async function homeVod() { + // 首页只加载第一个源的默认二级分类,避免一进源就把所有体育站数据全量请求一遍。 + const src = SOURCES[0]; + const list = await __callList(src, __firstCat(src.id), 1, false, {}); + return JSON.stringify({ code: 1, msg: '数据列表', page: 1, pagecount: 1, limit: list.length, total: list.length, list }); +} + +async function category(tid, pg, filter, extend) { + tid = String(tid || SOURCES[0].id); + extend = extend || {}; + const src = __srcById(tid) || SOURCES[0]; + const subTid = __pickExtendCat(extend) || __firstCat(src.id); + const list = await __callList(src, subTid, pg || 1, filter, extend); + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: list.length, total: list.length, list }); +} + +async function detail(id) { + const sp = __splitId(id); + const src = __srcById(sp.srcId); + if (!src) return JSON.stringify({ code: 1, list: [], page: 1, pagecount: 1, total: 0 }); + try { + const obj = __safeJson(await src.api.detail(sp.raw), {}); + const list = Array.isArray(obj.list) ? obj.list : []; + for (let i = 0; i < list.length; i++) { + if (list[i]) list[i].vod_id = __packId(src.id, list[i].vod_id || sp.raw); + } + obj.list = list; + return JSON.stringify(obj); + } catch (e) { + return JSON.stringify({ code: 1, list: [], page: 1, pagecount: 1, total: 0, msg: String(e && e.message || e) }); + } +} + +async function search(wd, quick, pg) { + const list = []; + for (let i = 0; i < SOURCES.length; i++) { + try { + const obj = __safeJson(await SOURCES[i].api.search(wd, quick, pg || 1), {}); + const part = __prefixList(obj.list || [], SOURCES[i]); + for (let j = 0; j < part.length; j++) list.push(part[j]); + } catch (e) {} + } + return JSON.stringify({ code: 1, msg: '数据列表', page: parseInt(pg) || 1, pagecount: 1, limit: list.length, total: list.length, list }); +} + +async function play(flag, id, flags) { + return JSON.stringify(await playerContent(flag, id, flags)); +} + +async function playerContent(flag, id, flags) { + const sp = __splitId(id); + const src = __srcById(sp.srcId); + if (!src) return { parse: 1, url: sp.raw || id }; + try { + if (src.api.playerContent) return await src.api.playerContent(flag, sp.raw, flags); + return __safeJson(await src.api.play(flag, sp.raw, flags), { parse: 1, url: sp.raw }); + } catch (e) { + return { parse: 1, url: sp.raw }; + } +} + +async function homeContent(filter) { return __safeJson(await home(filter), { class: [], filters: {} }); } +async function homeVideoContent() { return __safeJson(await homeVod(), { list: [] }); } +async function categoryContent(tid, pg, filter, extend) { return __safeJson(await category(tid, pg, filter, extend || {}), { list: [] }); } +async function detailContent(ids) { return __safeJson(await detail(ids), { list: [] }); } +async function searchContent(wd, quick, pg) { return __safeJson(await search(wd, quick, pg || 1), { list: [] }); } + +export function __jsEvalReturn() { + return { init, home, homeVod, category, search, detail, play, homeContent, homeVideoContent, categoryContent, detailContent, searchContent, playerContent }; +} diff --git a/js/咖啡体育.js b/js/咖啡体育.js new file mode 100644 index 0000000..17ec9b9 --- /dev/null +++ b/js/咖啡体育.js @@ -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 +}; \ No newline at end of file diff --git a/js/非凡.js b/js/非凡.js new file mode 100644 index 0000000..259d341 --- /dev/null +++ b/js/非凡.js @@ -0,0 +1,141 @@ +import { Crypto, load, _, jinja2 } from 'assets://js/lib/cat.js'; + +let key = 'ff'; +let HOST = 'https://ffzy5.tv'; +let siteKey = ''; +let siteType = 0; + +const UA = 'Mozilla/5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'; + +async function request(reqUrl, agentSp) { + let res = await req(reqUrl, { + method: 'get', + headers: { + 'User-Agent': agentSp || UA, + 'Referer': HOST + }, + }); + return res.content; +} + +async function init(cfg) { + siteKey = cfg.skey; + siteType = cfg.stype; +} + +async function home(filter) { + let classes = [{"type_id":1,"type_name":"电影"},{"type_id":2,"type_name":"连续剧"},{"type_id":3,"type_name":"综艺"},{"type_id":4,"type_name":"动漫"}]; + let filterObj = { + "2":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"2"},{"n":"短剧","v":"36"},{"n":"陆剧","v":"13"},{"n":"韩剧","v":"15"},{"n":"欧美剧","v":"16"},{"n":"港剧","v":"14"},{"n":"台剧","v":"21"},{"n":"日剧","v":"22"},{"n":"海外剧","v":"23"},{"n":"泰剧","v":"24"},{"n":"纪录片","v":"20"}]}], + "1":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"1"},{"n":"动作片","v":"6"},{"n":"喜剧片","v":"7"},{"n":"爱情片","v":"8"},{"n":"科幻片","v":"9"},{"n":"恐怖片","v":"10"},{"n":"剧情片","v":"11"},{"n":"战争片","v":"12"}]}], + "3":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"3"},{"n":"国综","v":"25"},{"n":"港综","v":"26"},{"n":"韩日综","v":"27"},{"n":"欧美综","v":"28"}]}], + "4":[{"key":"cateId","name":"类型","value":[{"n":"全部","v":"4"},{"n":"国漫","v":"29"},{"n":"日韩动漫","v":"30"},{"n":"欧美动漫","v":"31"},{"n":"港漫","v":"32"},{"n":"海外动漫","v":"33"}]}] + }; + + return JSON.stringify({ + class: classes, + filters: filterObj, + }); +} + +async function homeVod() {} + +async function category(tid, pg, filter, extend) { + if (pg <= 0) pg = 1; + let data = JSON.parse(await request(HOST + '/index.php/ajax/data?mid=1&tid=' + (extend.cateId || tid) + '&page=' + pg + '&limit=20')); + + let videos = []; + for (const vod of data.list) { + videos.push({ + vod_id: vod.vod_id, + vod_name: vod.vod_name, + vod_pic: vod.vod_pic, + vod_remarks: '', + }); + } + return JSON.stringify({ + page: parseInt(data.page), + pagecount: data.pagecount, + limit: 20, + total: data.total, + list: videos, + }); +} + +async function detail(id) { + var html = await request(HOST + '/index.php/vod/detail/id/' + id + '.html'); + var $ = load(html); + let pList = $('.people .right p'); + let getTxt = (label) => { + let text = ''; + pList.each((idx, el) => { + let t = $(el).text().trim(); + if (t.startsWith(label)) { + text = t.replace(label, '').trim(); + } + }); + return text; + }; + + var vod = { + vod_id: id, + vod_name: getTxt('片名:'), + vod_type: getTxt('类型:').replace(/ /g, ''), + vod_actor: getTxt('演员:'), + vod_director: getTxt('导演:'), + vod_pic: $('.people .left img').attr('src'), + vod_remarks: getTxt('状态:') || '', + vod_content: $('.vod_content').text().replace(/ /g, '').trim(), + }; + + const playlist = _.map($('div.ffm3u8 > li > a[target*=_blank]'), (it) => { + return it.attribs.title + '$' + it.attribs.href; + }); + + vod.vod_play_from = "非凡直达"; + vod.vod_play_url = playlist.join('#'); + return JSON.stringify({ + list: [vod], + }); +} + +async function play(flag, id, flags) { + return JSON.stringify({ + parse: 0, + url: id, + }); +} + +async function search(wd, quick, pg) { + if (pg <= 0) pg = 1; + let data = JSON.parse(await request(HOST + '/api.php/provide/vod/?wd=' + wd + '&pg=' + pg + '&ac=detail')); + + let videos = []; + for (const vod of data.list) { + videos.push({ + vod_id: vod.vod_id, + vod_name: vod.vod_name, + vod_pic: vod.vod_pic, + vod_remarks: '', + }); + } + return JSON.stringify({ + page: parseInt(data.page), + pagecount: data.pagecount, + limit: 20, + total: data.total, + list: videos, + }); +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + search: search, + }; +} \ No newline at end of file diff --git a/js/高清剧迷.js b/js/高清剧迷.js new file mode 100644 index 0000000..27a0b0c --- /dev/null +++ b/js/高清剧迷.js @@ -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 + } +} \ No newline at end of file