From 451386e76ee4b2cb07ac60ff5e65ee266adea93a Mon Sep 17 00:00:00 2001 From: gyjune <14752+gyjune@noreply.example.org> Date: Wed, 5 Aug 2026 04:12:06 +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 | 146 +++++++++++++++++++++++++++++---------------------- 1 file changed, 83 insertions(+), 63 deletions(-) diff --git a/js/茶杯狐.js b/js/茶杯狐.js index cabbd93..59baf47 100644 --- a/js/茶杯狐.js +++ b/js/茶杯狐.js @@ -41,41 +41,38 @@ function fixUrl(url) { return API_HOST + '/' + url; } -// ============ 解析视频列表 ============ +// ============ 解析视频列表(基于实际HTML结构) ============ function parseVideoList(html) { const videos = []; if (!html) return videos; - // 使用正则匹配 stui-vodlist__box 或 stui-vodlist__item - const itemPattern = /]*class="[^"]*stui-vodlist__box[^"]*"[^>]*>([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/g; + // 匹配每个 li 中的 stui-vodlist__box + const boxPattern = /]*class="stui-vodlist__box"[^>]*>([\s\S]*?)<\/div>\s*<\/div>\s*<\/div>/g; let match; - while ((match = itemPattern.exec(html)) !== null) { - const block = match[1]; + while ((match = boxPattern.exec(html)) !== null) { try { - // 提取链接和标题 - const linkMatch = block.match(/]*class="[^"]*stui-vodlist__thumb[^"]*"[^>]*title="([^"]*)"[^>]*href="([^"]*)"[^>]*>/); - if (!linkMatch) continue; - const title = linkMatch[1].trim(); - let href = linkMatch[2]; + const block = match[1]; + + // 提取 a 标签 - 包含 href, title, data-original + const aMatch = block.match(/]*class="[^"]*stui-vodlist__thumb[^"]*"[^>]*href="([^"]+)"[^>]*title="([^"]*)"[^>]*(?:data-original|src)="([^"]+)"[^>]*>/); + if (!aMatch) continue; + + let href = aMatch[1]; + const title = aMatch[2].trim(); + let pic = aMatch[3]; + if (!title || !href) continue; - // 提取图片 - let pic = ''; - const picMatch = block.match(/data-original="([^"]+)"/); - if (picMatch) { - pic = picMatch[1]; - } else { - const srcMatch = block.match(/src="([^"]+)"/); - if (srcMatch) pic = srcMatch[1]; + // 提取备注 (pic-text) + let remark = '更新中'; + const remarkMatch = block.match(/]*class="pic-text[^"]*"[^>]*>([^<]*)<\/span>/); + if (remarkMatch) { + remark = remarkMatch[1].trim(); } - // 提取备注 - let remark = ''; - const remarkMatch = block.match(/]*class="pic-text[^"]*"[^>]*>([^<]*)<\/span>/); - if (remarkMatch) remark = remarkMatch[1].trim(); - + // 修复 URL if (pic && !pic.startsWith('http')) pic = fixUrl(pic); if (href && !href.startsWith('http')) href = fixUrl(href); @@ -88,14 +85,14 @@ function parseVideoList(html) { } catch (e) {} } - // 备用解析方式 + // 备用解析:如果没有匹配到,尝试用更简单的方式 if (videos.length === 0) { - const pattern = /]*class="[^"]*stui-vodlist__thumb[^"]*"[^>]*title="([^"]*)"[^>]*(?:data-original|src)="([^"]*)"[^>]*href="([^"]*)"[^>]*>/g; - let m; - while ((m = pattern.exec(html)) !== null) { - const title = m[1].trim(); - let pic = m[2]; - let href = m[3]; + const altPattern = /]*class="[^"]*stui-vodlist__thumb[^"]*"[^>]*href="([^"]+)"[^>]*title="([^"]*)"[^>]*(?:data-original|src)="([^"]+)"[^>]*>/g; + let altMatch; + while ((altMatch = altPattern.exec(html)) !== null) { + let href = altMatch[1]; + const title = altMatch[2].trim(); + let pic = altMatch[3]; if (!title || !href) continue; if (pic && !pic.startsWith('http')) pic = fixUrl(pic); if (href && !href.startsWith('http')) href = fixUrl(href); @@ -124,18 +121,15 @@ const FILTERS = { '1': [ { key: 'class', name: '类型', value: [ { v: '1', n: '全部' }, - { v: '6', n: '动作片' }, - { v: '7', n: '喜剧片' }, - { v: '8', n: '爱情片' }, - { v: '9', n: '科幻片' }, - { v: '10', n: '恐怖片' }, - { v: '11', n: '剧情片' }, - { v: '12', n: '战争片' }, - { v: '13', n: '记录片' }, - { v: '14', n: '悬疑片' }, - { v: '15', n: '犯罪片' }, - { v: '16', n: '奇幻片' }, - { v: '31', n: '动画片' } + { v: 'dongzuopian', n: '动作片' }, + { v: 'xijupian', n: '喜剧片' }, + { v: 'aiqingpian', n: '爱情片' }, + { v: 'kehuanpian', n: '科幻片' }, + { v: 'kongbupian', n: '恐怖片' }, + { v: 'juqingpian', n: '剧情片' }, + { v: 'zhanzhengpian', n: '战争片' }, + { v: 'jilupian', n: '记录片' }, + { v: 'donghuapian', n: '动画片' } ]}, { key: 'area', name: '地区', value: [ { v: '0', n: '全部' }, @@ -145,9 +139,6 @@ const FILTERS = { { v: '美国', n: '美国' }, { v: '日本', n: '日本' }, { v: '韩国', n: '韩国' }, - { v: '英国', n: '英国' }, - { v: '法国', n: '法国' }, - { v: '德国', n: '德国' }, { v: '泰国', n: '泰国' }, { v: '印度', n: '印度' }, { v: '加拿大', n: '加拿大' }, @@ -179,11 +170,11 @@ const FILTERS = { '2': [ { key: 'class', name: '类型', value: [ { v: '2', n: '全部' }, - { v: '17', n: '国产剧' }, - { v: '21', n: '欧美剧' }, - { v: '20', n: '日韩剧' }, - { v: '18', n: '港台剧' }, - { v: '22', n: '海外剧' } + { v: 'guochanju', n: '国产剧' }, + { v: 'oumeiju', n: '欧美剧' }, + { v: 'rihanju', n: '日韩剧' }, + { v: 'gangtaiju', n: '港台剧' }, + { v: 'haiwaiju', n: '海外剧' } ]}, { key: 'area', name: '地区', value: [ { v: '0', n: '全部' }, @@ -277,22 +268,49 @@ async function category(tid, pg, filter, extend) { const classVal = (extend && extend.class) || ''; const areaVal = (extend && extend.area) || '0'; const yearVal = (extend && extend.year) || '0'; - // sort 仅用于排序,URL中不体现 + const sortVal = (extend && extend.sort) || 'time'; - // 构建URL: /movie/fenlei{tid}--{class}--{area}--{year}---{page}---.html - let c = (classVal && classVal !== '1' && classVal !== '2') ? classVal : ''; - let a = (areaVal !== '0') ? areaVal : ''; - let y = (yearVal !== '0') ? yearVal : ''; + // 构建筛选URL + let url = `${API_HOST}/vodshow`; - let url; - if (c || a || y) { - url = `${API_HOST}/movie/fenlei${tid}--${c}--${a}--${y}---${page}---.html`; + // 构建参数 + const params = []; + if (classVal && classVal !== '1' && classVal !== '2') { + params.push(`id/${classVal}`); + } + if (areaVal && areaVal !== '0') { + params.push(`area/${encodeURIComponent(areaVal)}`); + } + if (yearVal && yearVal !== '0') { + params.push(`year/${yearVal}`); + } + if (sortVal && sortVal !== 'time') { + params.push(`by/${sortVal}`); + } + + // 如果没有任何筛选,使用默认分类页 + if (params.length === 0) { + if (tid === '1') { + url = `${API_HOST}/movie/fenlei1---${page}.html`; + } else if (tid === '2') { + url = `${API_HOST}/movie/fenlei2---${page}.html`; + } else if (tid === '3') { + url = `${API_HOST}/movie/fenlei3---${page}.html`; + } else if (tid === '4') { + url = `${API_HOST}/movie/fenlei4---${page}.html`; + } else { + url = `${API_HOST}/vodshow/id/${tid}/page/${page}.html`; + } } else { - url = `${API_HOST}/movie/fenlei${tid}---${page}.html`; + // 有筛选条件:/vodshow/id/dongzuopian/area/大陆/year/2024/by/time/page/2.html + const idParam = classVal && classVal !== '1' && classVal !== '2' ? `id/${classVal}` : `id/${tid}`; + const allParams = [idParam, ...params]; + url = `${API_HOST}/vodshow/${allParams.join('/')}/page/${page}.html`; } const html = await fetchHtml(url); const videos = parseVideoList(html); + return JSON.stringify({ list: videos, page: page, @@ -477,7 +495,6 @@ async function play(flag, id, flags) { try { const html = await fetchHtml(playUrl); - // 提取真实视频地址 let realUrl = null; // 匹配 JSON 中的 url @@ -495,13 +512,16 @@ async function play(flag, id, flags) { } } - // 直接匹配 m3u8 URL if (!realUrl) { const m3u8Match = html.match(/https?:\/\/[^"'\s<>]+\.m3u8[^"'\s<>]*/i); if (m3u8Match) realUrl = m3u8Match[0]; } - // 匹配 iframe 并递归 + if (!realUrl) { + const mp4Match = html.match(/https?:\/\/[^"'\s<>]+\.mp4[^"'\s<>]*/i); + if (mp4Match) realUrl = mp4Match[0]; + } + if (!realUrl) { const iframeMatch = html.match(/]*src=["']([^"']+)["']/i); if (iframeMatch) { @@ -536,8 +556,8 @@ export function __jsEvalReturn() { home: home, homeVod: homeVod, category: category, - detail: detail, search: search, + detail: detail, play: play }; } \ No newline at end of file