From c40b5449b7a95c1c94b1d7e38d920ca498d27faf Mon Sep 17 00:00:00 2001 From: XC-Pro <19840+xc-pro@noreply.example.org> Date: Fri, 14 Aug 2026 11:23:32 +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=8Copen=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- open/91live_zb.js | 268 ++++++ open/97_search.js | 549 +++++++++++ open/baidu_dj.js | 229 +++++ open/djus_search.js | 379 ++++++++ open/duanju_juhe.js | 2178 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 3603 insertions(+) create mode 100644 open/91live_zb.js create mode 100644 open/97_search.js create mode 100644 open/baidu_dj.js create mode 100644 open/djus_search.js create mode 100644 open/duanju_juhe.js diff --git a/open/91live_zb.js b/open/91live_zb.js new file mode 100644 index 0000000..f4e873b --- /dev/null +++ b/open/91live_zb.js @@ -0,0 +1,268 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '91电视[直]', + lang: 'cat', +}) +*/ + +import { Crypto as CryptoJS } from 'assets://js/lib/cat.js'; + +let host = 'http://sj.91kds.cn'; +let siteName = '91电视', siteKey = '', siteType = 0; + +let UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (Chrome/126.0.0.0 Safari/537.36)"; + +const headers = { + 'User-Agent': UA, + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' +}; + +function init(cfg) { + siteName = cfg.skey?.split('_')[1] || cfg.skey || '91电视'; + siteKey = cfg.skey; + siteType = cfg.stype; + + if (cfg && typeof cfg === 'string') { + host = cfg; + } else if (cfg && typeof cfg === 'object') { + let ext = cfg.ext; + if (ext && typeof ext === 'object' && Object.keys(ext).length) { + host = ext.host || ext.hosturl || ext.url || ext.site; + } + } +} + +function safeJSONParse(str, defaultValue = {}) { + if (!str || typeof str === 'object') return str || defaultValue; + try { + return JSON.parse(str); + } catch { + return defaultValue; + } +} + +async function request(url, options = {}) { + const reqHeaders = { ...headers, ...options.headers }; + let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' : + reqHeaders['Content-Type']?.includes('form') ? 'form' : ''; + + try { + const response = await req(url, { + method: options.method || 'GET', + headers: reqHeaders, + data: options.data, + postType: postType, + timeout: options.timeout || 15000 + }); + return response?.content || response?.data || response; + } catch { + return null; + } +} + +async function home(filter) { + const classes = [ + { type_id: "央视", type_name: "央视" }, { type_id: "卫视", type_name: "卫视" }, + { type_id: "高清", type_name: "高清" }, { type_id: "4K", type_name: "4K" }, + { type_id: "影视", type_name: "影视" }, { type_id: "体育", type_name: "体育" }, + { type_id: "动漫", type_name: "动漫" }, { type_id: "财经", type_name: "财经" }, + { type_id: "综艺", type_name: "综艺" }, { type_id: "教育", type_name: "教育" }, + { type_id: "新闻", type_name: "新闻" }, { type_id: "纪录", type_name: "纪录" }, + { type_id: "国际", type_name: "国际" }, { type_id: "网络", type_name: "网络" }, + { type_id: "购物", type_name: "购物" }, { type_id: "虎牙", type_name: "虎牙" } + ]; + + const filters = {}; + classes.forEach(cls => { filters[cls.type_id] = []; }); + + return JSON.stringify({ class: classes, filters: filters }); +} + +async function homeVod() { + return await category('湖北', 1, null, {}); +} + +async function category(tid, pg, filter, extend) { + try { + const url = `${host}/api/get_channel.php?id=${tid}`; + const html = await request(url); + + if (!html || !html.includes('ename')) { + return JSON.stringify({ list: [], page: 1, pagecount: 1, limit: 20, total: 0 }); + } + + const list = safeJSONParse(html); + const nwtime = Math.floor(Date.now() / 1000); + + const videos = list.map(item => { + const enamee = item.ename; + const srcKey = enamee + "com.jiaoxiang.fangnaleahkajfkahlajjaflfakhfakfbuyaozaigaolefuquqikangbuzhu2.3.4fu:ck:92:92:ff" + nwtime + "20240918"; + const sign = CryptoJS.MD5(srcKey).toString(); + const detailUrl = `http://sjapi1.91kds.cn/api/get_source.php?ename=${enamee}&app=com.jiaoxiang.fangnale&version=2.3.4&mac=fu:ck:92:92:ff&nwtime=${nwtime}&sign=${sign}&ev=20240918`; + + return { + vod_id: detailUrl + '@' + item.name, + vod_name: item.name, + vod_pic: item.icon, + vod_remarks: '直播' + }; + }); + + return JSON.stringify({ list: videos, page: 1, pagecount: 1, limit: 20, total: videos.length }); + } catch (e) { + return JSON.stringify({ list: [], page: 1, pagecount: 1, limit: 20, total: 0 }); + } +} + +async function detail(id) { + try { + const [purl, vod_name] = id.split('@'); + const html = await request(purl); + const data = safeJSONParse(html); + + const vod = { + vod_id: purl, + vod_name: vod_name || data.name || data.title || "直播频道", + vod_pic: data.icon || "", + vod_content: data.desc || "暂无简介", + vod_remarks: "直播" + }; + + let list = data.liveSource || []; + let names = data.liveSourceName || []; + let playFrom = []; + let playUrl = []; + let seen = new Set(); + let lineCounter = 1; + + list.forEach((item, j) => { + let rawInput = item; + let inputUrl = rawInput.replace(/^kdsvod:\/\//, ''); + let urlName = names[j] || '线路' + lineCounter; + + if (inputUrl.includes('pwd=jsdecode') && inputUrl.includes('id=')) { + let parts = inputUrl.split('?'); + let baseUrl = parts[0]; + let queryStr = parts[1] || ''; + let queryObj = {}; + queryStr.split('&').forEach(kv => { + let t = kv.split('='); + if (t[0]) queryObj[t[0]] = decodeURIComponent(t[1] || ''); + }); + + let id = queryObj['id']; + let bt = queryObj['bt'] || null; + let coreKey = (bt || '') + '_' + id; + + if (seen.has(coreKey)) return; + seen.add(coreKey); + + let params = { + app: 'com.jiaoxiang.fangnale', + version: '2.3.4', + mac: 'fu:ck:92:92:ff', + utk: '', + nwtime: Math.floor(Date.now() / 1000), + ev: '20250113' + }; + + let appendStr = 'ahkajfkahlajjaflfakhfakfbuyaozaigaolefuquqikangbuzhu'; + let signStr = id; + Object.keys(params).forEach(key => { + if (key === 'tmk') return; + if (key === 'app') signStr += params[key] + appendStr; + else signStr += params[key]; + }); + + params.sign = CryptoJS.MD5(signStr).toString(); + let finalQuery = []; + if (bt !== null) finalQuery.push('bt=' + bt); + finalQuery.push('id=' + id); + Object.keys(params).forEach(k => { + finalQuery.push(k + '=' + encodeURIComponent(params[k])); + }); + + let finalUrl = baseUrl + '?' + finalQuery.join('&'); + let lineName = '线路' + lineCounter; + playFrom.push(lineName); + playUrl.push(urlName + '$' + finalUrl); + lineCounter++; + } else { + let videoUrl; + if (inputUrl.startsWith('htmlplay://')) { + videoUrl = inputUrl.replace('htmlplay://', '').split('#')[0]; + } else { + videoUrl = inputUrl; + } + + let urlKey = videoUrl.split('?')[0]; + if (seen.has(urlKey)) return; + seen.add(urlKey); + + let referer = ''; + if (inputUrl.includes('@@referer=')) { + let tmp = inputUrl.split('@@referer='); + videoUrl = tmp[0]; + referer = tmp[1] || ''; + } + + let lineName = '线路' + lineCounter; + + if (referer) { + let playObj = JSON.stringify({ url: videoUrl, header: { Referer: referer } }); + playFrom.push(lineName); + playUrl.push(urlName + '$' + playObj); + } else { + playFrom.push(lineName); + playUrl.push(urlName + '$' + videoUrl); + } + lineCounter++; + } + }); + + vod.vod_play_from = playFrom.join('$$$'); + vod.vod_play_url = playUrl.join('$$$'); + + return JSON.stringify({ list: [vod] }); + } catch (e) { + return JSON.stringify({ list: [] }); + } +} + +async function play(flag, id, flags) { + return JSON.stringify({ parse: 0, url: id, header: {} }); +} + +async function search(wd, quick, pg = "1") { + try { + const nwtime = Math.floor(Date.now() / 1000); + const srcKey = "4954af3c86d8bc0b766afee71503d860" + nwtime + "f8dd806a73202456eb6e782c1c4aecfc"; + const sign = CryptoJS.MD5(srcKey).toString(); + + const searchUrl = `${host}/api/get_search.php?id=${encodeURIComponent(wd)}`; + const html = await request(searchUrl); + + if (!html || !html.includes('ename')) { + return JSON.stringify({ list: [], page: parseInt(pg), pagecount: 0, limit: 20, total: 0 }); + } + + const list = safeJSONParse(html); + const videos = list.map(item => ({ + vod_id: `${host}/api/get_source.php?ename=${item.ename}@${item.name}`, + vod_name: item.name, + vod_pic: item.icon, + vod_remarks: item.path || '' + })); + + return JSON.stringify({ list: videos, page: parseInt(pg), pagecount: 1, limit: 20, total: videos.length }); + } catch (e) { + return JSON.stringify({ list: [], page: parseInt(pg), pagecount: 0, limit: 20, total: 0 }); + } +} + +export function __jsEvalReturn() { + return { init, home, homeVod, category, detail, play, search }; +} \ No newline at end of file diff --git a/open/97_search.js b/open/97_search.js new file mode 100644 index 0000000..ac9dcd8 --- /dev/null +++ b/open/97_search.js @@ -0,0 +1,549 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 1, + title: '网盘搜索[搜]', + lang: 'ds' +}) +*/ +import '../lib/htmlParser.js'; +import { Quark, Baidu, UC } from "../lib/pans.js"; + +// ==================== 全局变量 ==================== +let siteName = '97网盘搜索', siteKey = '', siteType = 0; +let baseUrl = ''; +let DEBUG = 0; // 默认关闭调试打印 + +// ==================== 开关配置 (1=开启,0=关闭)==================== +let quarkInfinite = 0; // 夸克网盘无限画质开关 (1=开启无限画质, 0=关闭) +let quarkOrig = 1; // 夸克网盘原画开关 (1=开启原画, 0=关闭) +let quarkTransfer = 1; // 夸克网盘转存开关 (1=开启转存获取画质, 0=关闭) +let enableProxy = 0; // 全局代理开关 (1=开启代理, 0=关闭) +// ================================================ + +let UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"; + +let headers = { + "User-Agent": UA, + "Accept": "application/json, text/plain, */*", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" +}; + +let proxyurl = 'http://127.0.0.1:2525/proxy?url='; +let downThreads = '20'; + +// 网盘图标映射 +const diskIconMap = [ + { img: 'https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/夸克.png', type: '夸克网盘', pan_type: 2 }, + { img: 'https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/百度.png', type: '百度网盘', pan_type: 1 } +]; +let defaultImg = `https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/网盘搜索.png`; + +// API基础地址 +let host = 'https://pansoo.cn'; + +// Cookie缓存 +let cachedCookie = null; +let cookieCacheTime = 0; +let COOKIE_CACHE_DURATION = 300000; + +// 搜索最大翻页数 +let maxPages = 5; + +// ==================== 安全JSON解析 ==================== +function safeJSONParse(str, defaultValue = {}) { + if (!str || typeof str === 'object') return str || defaultValue; + try { + return JSON.parse(str); + } catch { + return defaultValue; + } +} + +// ==================== 统一请求函数 ==================== +async function request(url, options = {}) { + const reqHeaders = { ...headers, ...options.headers }; + let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' : + reqHeaders['Content-Type']?.includes('form') ? 'form' : ''; + + try { + const response = await req(url, { + method: options.method || 'GET', + headers: reqHeaders, + data: options.data, + postType: postType, + timeout: options.timeout || 15000 + }); + return response?.content || response?.data || response; + } catch { + return null; + } +} + +// ==================== 画质映射 ==================== +function mapResolution(res) { + const map = { + 'low': '流畅', 'normal': '标清', 'high': '高清', 'super': '超清', + '4k': '4K', '2k': '2K', 'hdr': 'HDR', 'dolby_vision': 'HDR', + 'M3U8_AUTO_480': '480P', 'M3U8_AUTO_720': '720P', 'M3U8_AUTO_1080': '1080P', + 'M3U8_AUTO_2K': '2K', 'M3U8_AUTO_4K': '4K' + }; + return map[res] || res; +} + +// ==================== 远程配置加载 ==================== +async function loadRemoteConfig(cfgUrl) { + try { + console.log(`[${siteName}] 请求远程配置: ${cfgUrl}`); + const res = await request(cfgUrl, { timeout: 10000 }); + if (res) { + const remoteCfg = safeJSONParse(res); + // 设置Cookie + if (remoteCfg.quark_cookie?.length > 10) Quark.cookie = remoteCfg.quark_cookie; + if (remoteCfg.baidu_cookie?.length > 10) Baidu.cookie = remoteCfg.baidu_cookie; + if (remoteCfg.uc_cookie?.length > 10) UC.cookie = remoteCfg.uc_cookie; + if (remoteCfg.uc_token?.length > 10) UC.token = remoteCfg.uc_token; + // 可选:其他配置 + if (remoteCfg.threads) downThreads = remoteCfg.threads; + if (remoteCfg.enableProxy !== undefined) enableProxy = remoteCfg.enableProxy; + if (remoteCfg.infinite !== undefined) quarkInfinite = remoteCfg.infinite; + if (remoteCfg.quark_original !== undefined) quarkOrig = remoteCfg.quark_original; + if (remoteCfg.quark_transfer !== undefined) quarkTransfer = remoteCfg.quark_transfer; + console.log(`[${siteName}] 远程配置加载成功`); + return true; + } + } catch (e) { + console.log(`[${siteName}] 远程配置加载失败: ${e.message}`); + } + return false; +} + +// ==================== 主程序函数 ==================== + +async function init(cfg) { + siteName = cfg.skey?.split('_')[1] || cfg.skey || siteName; + siteKey = cfg.skey; + siteType = cfg.stype; + + let ext = cfg.ext || cfg; + if (typeof ext === 'string') ext = decodeURIComponent(ext); + + if (typeof ext === 'string') { + await loadRemoteConfig(ext); + } + host = host.endsWith('/') ? host : host + '/'; + +} + +function home() { + return JSON.stringify({ + class: [ + { type_id: 'search', type_name: '这是一个搜索源' } + ], + }); +} + +async function homeVod() { + return await category('search', 1, null, null); +} + +async function category(tid, pg, filter, ext) { + if (tid === 'search') { + return JSON.stringify({ + list: [{ + vod_id: 'only_search', + vod_name: '🔍 网盘资源搜索', + vod_pic: defaultImg, + }] + }); + } + return JSON.stringify({ list: [] }); +} + +/** + * 详情页 - 获取资源分享链接并解析 + */ +async function detail(id) { + // 解码并解析 JSON + let detailObj; + try { + let decoded = decodeURIComponent(id); + detailObj = safeJSONParse(decoded); + } catch (e) { + // 兼容旧格式(如果还有用竖线分隔的旧数据) + let parts = id.split('|'); + detailObj = { + rid: parts[0], + name: parts[1] || '网盘资源', + type: parts[2] || '2' + }; + } + + let resourceId = detailObj.rid; + let vod_name = detailObj.name; + let panType = detailObj.type; + + log('详情', `${resourceId} - ${vod_name} - panType:${panType}`); + + let play_from = []; + let play_url = []; + let play_pic = []; + + let cookie = await getDynamicCookie(); + let apiHeaders = { + "User-Agent": headers["User-Agent"], + "Accept": "application/json, text/plain, */*", + "Referer": `https://pansoo.cn/` + }; + if (cookie) apiHeaders["Cookie"] = cookie; + + let checkUrl = `${host}/api/check_resource_status?resource_id=${resourceId}&pan_type=${panType}`; + let response = await request(checkUrl, { headers: apiHeaders, timeout: 15000 }); + + let shareUrl = null; + if (response) { + try { + let data = safeJSONParse(response); + if (data.share_url) shareUrl = data.share_url; + } catch (e) {} + } + + if (shareUrl && shareUrl.startsWith('http')) { + let counters = { '夸克': 1, '百度': 1, '优汐': 1 }; + + if (/pan.quark.cn/.test(shareUrl)) { + let shareData = Quark.getShareData(shareUrl); + if (shareData) { + let files = await Quark.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.pdir_fid || '', v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`夸克#${counters['夸克']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } else if (/pan.baidu.com/.test(shareUrl)) { + let shareData = Baidu.getShareData(shareUrl); + if (shareData) { + let files = await Baidu.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + let info = { surl: shareData.surl, pwd: shareData.pwd || '' }; + return `${v.name}${size}$${[v.path, v.uk, v.shareid, v.fsid, JSON.stringify(info)].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`百度#${counters['百度']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } else if (/drive.uc.cn|\.uc/.test(shareUrl)) { + let shareData = UC.getShareData(shareUrl); + if (shareData) { + let files = await UC.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`优汐#${counters['优汐']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } + } + + if (play_from.length === 0) { + play_from.push('提示'); + play_url.push(shareUrl ? `分享链接: ${shareUrl}` : `获取分享链接失败,资源ID: ${resourceId}`); + play_pic.push(''); + } + + // 构建简介内容,加入网盘链接 + let contentParts = [`资源ID: ${resourceId}`, `网盘类型: ${getPanTypeName(parseInt(panType))}`]; + if (shareUrl) { + contentParts.push(`网盘链接: ${shareUrl}`); + } + let vod_content = contentParts.join(' | '); + + let vod = { + vod_id: id, + vod_name: vod_name, + vod_pic: '', + vod_content: vod_content, + vod_remarks: play_from.length > 0 ? (play_from[0].split('#')[0] + '可播放') : '解析失败', + vod_play_from: play_from.join('$$$'), + vod_play_url: play_url.join('$$$'), + vod_play_pic: play_pic.join('$$$'), + vod_play_pic_ratio: 1.0 + }; + + return JSON.stringify({ list: [vod] }); +} + +/** + * 搜索 - 批量并发请求 + 关键词匹配过滤 + */ +async function search(wd, quick, pg) { + let page = pg || 1; + log('搜索', `${wd} - page${page}`); + + let cookie = await getDynamicCookie(); + let searchHeaders = { + "User-Agent": headers["User-Agent"], + "Accept": "application/json, text/plain, */*", + "Referer": `https://pansoo.cn/search?q=${encodeURIComponent(wd)}&type=online` + }; + if (cookie) searchHeaders["Cookie"] = cookie; + + // 构建批量请求 + let urls = []; + for (let p = page; p < page + maxPages; p++) { + urls.push({ + url: `${host}/api/search?keyword=${encodeURIComponent(wd)}&pan_types=1,2&page=${p}&limit=30&file_type=video`, + options: { headers: searchHeaders, timeout: 15000 } + }); + } + + let results = await batchFetch(urls); + let allList = []; + + for (let content of results) { + if (!content) continue; + try { + let data = safeJSONParse(content); + if (data.status === 'success' && data.results) { + for (let resource of data.results) { + let title = resource.title; + if (!title || title.length < 2) continue; + + let panType = resource.pan_type; + // 如果 pan_type 无效,默认使用 2(夸克网盘) + if (panType === undefined || panType === null || panType === '') { + panType = 2; + } + + // 使用 JSON 编码构建 detailId,避免标题中的特殊字符(如 |)导致解析错误 + let detailObj = { + rid: resource.resource_id, + name: title, + type: panType + }; + let detailId = encodeURIComponent(JSON.stringify(detailObj)); + + allList.push({ + vod_id: detailId, + vod_name: title, + vod_pic: getDiskIconByPanType(panType), + vod_content: resource.text_content || '', + vod_remarks: `${getPanTypeName(panType)}${resource.file_size && resource.file_size !== '未知大小' ? ' | ' + resource.file_size : ''}` + }); + } + } + } catch (e) {} + } + + // 关键词过滤 + let filteredList = allList.filter(item => new RegExp(wd, "i").test(item.vod_name)); + log('搜索结果', `${filteredList.length}/${allList.length}`); + + return JSON.stringify({ + page: page, + pagecount: maxPages, + limit: filteredList.length, + total: filteredList.length, + list: filteredList + }); +} + +/** + * 播放 + */ +async function play(flag, id, flags) { + let ids = id.split('*'); + let urls = []; + + const addUrl = (name, u) => { + if (u) urls.push(name, proxyUrl(u) + `&thread=${downThreads}`); + }; + + // 夸克网盘 + if (flag.startsWith('夸克') && ids.length >= 4) { + let [shareId, stoken, fid, share_fid_token] = ids; + let header = { + 'User-Agent': headers['User-Agent'], + 'origin': 'https://pan.quark.cn', + 'referer': 'https://pan.quark.cn/', + 'Cookie': Quark.cookie + }; + + // 无限画质(免转存直链) + if (quarkInfinite == 1) { + const tokenUrls = await Quark.getUrl(shareId, stoken, fid, share_fid_token); + tokenUrls?.forEach(item => { if (item?.url) addUrl("无限" + (item.name || ''), item.url); }); + } + + // 转存逻辑(原画和多分辨率) + let shouldTransfer = quarkTransfer; + if (quarkInfinite != 1 && quarkOrig != 1 && quarkTransfer != 1) { + shouldTransfer = 1; + } + + if (shouldTransfer == 1) { + // 原画 + if (quarkOrig == 1) { + let down = await Quark.getDownload(shareId, stoken, fid, share_fid_token, true); + if (down && down.error) { + const errorMsg = down.message || '未知错误'; + return JSON.stringify({ parse: 0, msg: `夸克: ${errorMsg}`, header: header }); + } + if (down?.download_url) addUrl("原画", down.download_url); + } + + // 转码画质 + let transcoding = await Quark.getLiveTranscoding(shareId, stoken, fid, share_fid_token); + if (transcoding?.length) { + transcoding.filter(t => t.accessable).forEach(t => { + if (t?.video_info?.url) { + addUrl(mapResolution(t.resolution), t.video_info.url); + } + }); + } + } + + if (urls.length) { + return JSON.stringify({ parse: 0, url: urls, header: header }); + } + } + + // 百度网盘 + if (flag.startsWith('百度') && ids.length >= 5) { + let [path, uk, shareid, fsid, shareDataStr] = ids; + let shareData = safeJSONParse(shareDataStr); + + let original = await Baidu.getAppShareUrl(path, uk, shareid, fsid, shareData); + if (original && !original.error) { + addUrl("原画", original); + } else { + const errorMsg = original?.message || '获取链接失败,请检查Cookie是否有效'; + return JSON.stringify({ parse: 0, msg: `百度: ${errorMsg}`, header: headers }); + } + + if (urls.length) { + return JSON.stringify({ + parse: 0, + url: urls, + header: { "User-Agent": 'netdisk;P2SP;2.2.91.136;android-android;' } + }); + } + } + + // 优汐网盘 + if (flag.startsWith('优汐') && ids.length >= 4) { + let [shareId, stoken, fid, share_fid_token] = ids; + + let down = await UC.getDownload(shareId, stoken, fid, share_fid_token, true); + if (down && down.error) { + const errorMsg = down.message || '未知错误'; + return JSON.stringify({ parse: 0, msg: `优汐: ${errorMsg}`, header: headers }); + } + if (down?.length) { + down.forEach(item => { if (item?.url) addUrl(mapResolution(item.name), item.url); }); + } + + if (urls.length) { + return JSON.stringify({ parse: 0, url: urls }); + } + } + + // 直接链接 + if (id.startsWith('http')) { + urls.push('原画', proxyUrl(id) + `&thread=${downThreads}`); + return JSON.stringify({ parse: 0, url: urls }); + } + return JSON.stringify({ parse: 0, msg: `解析分享链接失败, 分享链接可能已失效`, header: headers }); + +} + +// ==================== Cookie获取函数 ==================== + +async function getDynamicCookie() { + let now = Date.now(); + if (cachedCookie && cookieCacheTime > now) return cachedCookie; + + try { + // 使用 req 直接请求,以便获取响应头 + const response = await req(host, { + method: 'GET', + headers: { "User-Agent": headers["User-Agent"] }, + timeout: 10000 + }); + + let cookie = null; + if (response && response.headers) { + let setCookie = response.headers['set-cookie'] || response.headers['Set-Cookie']; + if (setCookie) { + // 处理可能是数组的情况 + let cookieStr = Array.isArray(setCookie) ? setCookie.join('; ') : setCookie; + let match = cookieStr.match(/(__cf_bm=[^;]+)/); + if (match) cookie = match[1]; + } + } + if (cookie) { + cachedCookie = cookie; + cookieCacheTime = now + COOKIE_CACHE_DURATION; + } + return cookie; + } catch (e) { + return null; + } +} + +// ==================== 工具函数 ==================== + +function log(tag, msg) { + if (DEBUG) console.log(`【${siteName}-${tag}】 ${msg}`); +} + +function getDiskIconByPanType(panType) { + let matched = diskIconMap.find(item => item.pan_type === parseInt(panType)); + return matched ? matched.img : defaultImg; +} + +function getPanTypeName(panType) { + let matched = diskIconMap.find(item => item.pan_type === parseInt(panType)); + return matched ? matched.type : '网盘资源'; +} + +function formatSize(bytes) { + let size = typeof bytes === 'string' ? parseInt(bytes, 10) : bytes; + if (!size || isNaN(size) || size <= 0) return '0 B'; + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let i = 0; + while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; } + return size.toFixed(2) + ' ' + units[i]; +} + +function proxyUrl(url) { + if (!url) return ''; + if (enableProxy && proxyurl) return proxyurl + encodeURIComponent(url); + return url; +} + +async function proxy(params) { + let url = params.url; + return [302, '', '', { 'Location': url }]; +} + +// ==================== 导出 ==================== +export function __jsEvalReturn() { + return { init, home, homeVod, category, detail, play, search, proxy }; +} \ No newline at end of file diff --git a/open/baidu_dj.js b/open/baidu_dj.js new file mode 100644 index 0000000..606974a --- /dev/null +++ b/open/baidu_dj.js @@ -0,0 +1,229 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 1, + title: '百度短剧', + lang: 'cat' +}) +*/ +import { Crypto as CryptoJS } from 'assets://js/lib/cat.js'; + +let siteName = '百度短剧', siteKey = '', siteType = 0; + +let UA = "Mozilla/5.0 (Linux; Android 11; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.91 Mobile Safari/537.36"; +let clarity_order = { '蓝光': 1, '超清': 2, '标清': 3 }; + +let rule = { + host: 'https://mbd.baidu.com', + detailHost: 'https://sv.baidu.com', + listUrl: '/feedapi/v1/videoserver/playlets/list?service=bdbox', + searchUrl: '/feedapi/v1/videoserver/playlets/search?service=bdbox', + detailUrl: '/haokan/ui-video/playlet/rec/detail?log=vhk&tn=1020970b&ctn=1008350n&blur=1', + playUrl: '/appui/api?cmd=video/relate&log=vhk&tn=1020970b&ctn=1008350n&blur=1', +}; + +const headers = { + 'User-Agent': UA, + 'Content-Type': 'application/x-www-form-urlencoded', + 'Accept': 'application/json' +}; + +function init(cfg) { + siteName = cfg.skey?.split('_')[1] || cfg.skey || '百度短剧'; + siteKey = cfg.skey; + siteType = cfg.stype; +} + +function safeJSONParse(str, defaultValue = {}) { + if (!str || typeof str === 'object') return str || defaultValue; + try { + return JSON.parse(str); + } catch { + return defaultValue; + } +} + +async function request(url, options = {}) { + const reqHeaders = { ...headers, ...options.headers }; + let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' : + reqHeaders['Content-Type']?.includes('form') ? 'form' : ''; + + try { + const response = await req(url, { + method: options.method || 'GET', + headers: reqHeaders, + data: options.data, + postType: postType, + timeout: options.timeout || 15000 + }); + const content = response?.content || response?.data || response; + return typeof content === 'object' ? content : safeJSONParse(content); + } catch { + return null; + } +} + +function home(filter) { + let he = ["全部", "新剧", "限时免费", "精选", "独播"]; + let ticailist = [ + "神医", "连续剧", "都市", "现代言情", "异能", "逆袭", "甜宠", "总裁", "萌宝", "战神", "宫斗宅斗", "神豪", + "虐恋", "闪婚", "玄幻", "穿越重生", "年代", "家庭伦理", "古代言情", "武侠武打", "赘婿", "单元剧", "青春校园", + "历史架空", "王妃", "鉴宝", "科幻", "军旅战争", "种田" + ]; + + let classes = he.map(name => ({ type_id: name, type_name: name })); + classes = classes.concat(ticailist.map(name => ({ + type_id: name === "全部" ? "全部题材" : name, + type_name: name + }))); + return JSON.stringify({ class: classes, filters: {} }); +} + +async function homeVod() { + const categoryResult = await category('新剧', 1, {}, {}); + const list = safeJSONParse(categoryResult).list || []; + return JSON.stringify({ list: list.slice(0, 12) }); +} + +async function category(tid, pg, filter, extend) { + pg = pg <= 0 ? 1 : pg; + let sub = ["新剧", "限时免费", "精选", "独播"].includes(tid) ? tid : "新剧"; + let tcsub = (tid === "全部" || tid === "全部题材") ? "" : tid; + let t = Math.floor(Date.now() / 1000); + let version = await md5(t + "v2"); + + let postData = { + 'data': JSON.stringify({ + "data": { + "extRequest": { "flow_tabid": "13" }, + "from": "feed", + "page": "channel_video_landing", + "pd": "feed", + "refreshIndex": pg, + "cursor": "", + "theme": "", + "timestamp": t, + "version": version, + "themes": [ + { "kind": "综合", "names": [sub] }, + { "kind": "题材", "names": [tcsub] } + ] + } + }) + }; + + const res = await request(`${rule.host}${rule.listUrl}`, { method: 'POST', data: postData }); + let videos = (res?.data?.items || []).map(it => ({ + vod_id: it.collId || '', + vod_name: it.title || '未知标题', + vod_pic: it.img || '', + vod_remarks: it.updateStatus || '', + vod_content: it.description || '' + })); + + return JSON.stringify({ + page: pg, + pagecount: pg + 1, + limit: videos.length, + total: videos.length * (pg + 1), + list: videos + }); +} + +async function detail(id) { + const res = await request(`${rule.detailHost}${rule.detailUrl}`, { + method: 'POST', + data: { playlet_id: id, vid: "undefined" } + }); + const dthtml = res?.data; + const vids = dthtml?.vid_list || []; + if (!vids.length) return JSON.stringify({ list: [] }); + + const playArr = vids.map((vid, index) => `第${index + 1}集$${vid}`); + const vod = { + vod_id: id, + vod_name: dthtml.playlet_title || '未知剧名', + vod_pic: dthtml.playlet_poster || '', + vod_content: dthtml.description || '', + vod_remarks: `共${vids.length}集 热度值:${dthtml.hot_value || 0}`, + vod_director: dthtml.tag_text || '', + vod_year: dthtml.create_time || '', + vod_play_from: "百度短剧", + vod_play_url: playArr.join('#') + }; + + return JSON.stringify({ list: [vod] }); +} + +async function play(flag, id, flags) { + let res = await request(`${rule.detailHost}${rule.playUrl}`, { + method: 'POST', + data: { method: "post", vid: id } + }); + const video = res?.["video/relate"]?.data?.cur_video; + if (!video?.clarityUrl) return JSON.stringify({ parse: 0, url: '', msg: '获取播放链接失败' }); + + const urls = video.clarityUrl.filter(item => item?.url).map(item => ({ + title: item.title, + url: item.url, + order: clarity_order[item.title] || 999 + })).sort((a, b) => a.order - b.order); + + if (!urls.length) return JSON.stringify({ parse: 0, url: '', msg: '暂无可用播放地址' }); + + const flat = urls.flatMap(item => [item.title, item.url]); + return JSON.stringify({ + parse: 0, + url: flat, + header: { 'User-Agent': UA, 'Referer': rule.host } + }); +} + +async function search(wd, quick, pg) { + pg = pg <= 0 ? 1 : pg; + let postData = { + 'data': JSON.stringify({ + "query": wd, + "page": pg, + "attribute": ["title"], + "fe_page_type": "search", + "extra": { + "tab_id": "216", + "flow_tabid": "13", + "shortplay_source": "feed", + "from": "feed", + "tab_type": "搜索", + "sub_template": "playlet_search_result" + } + }) + }; + + const res = await request(`${rule.host}${rule.searchUrl}`, { + method: 'POST', + data: postData + }); + let videos = (res?.data?.itemList || []).map(it => ({ + vod_id: it.nid?.split("_")[1] || '', + vod_name: it.title || '未知标题', + vod_pic: it.img || '', + vod_remarks: (it.collNum || '0') + '集', + vod_content: it.description || '' + })); + + return JSON.stringify({ + page: pg, + pagecount: pg + 1, + limit: videos.length, + total: videos.length * (pg + 1), + list: videos + }); +} + +async function md5(str) { + return CryptoJS.MD5(str).toString(CryptoJS.enc.Hex).toLowerCase(); +} + +export function __jsEvalReturn() { + return { init, home, homeVod, category, detail, play, search }; +} \ No newline at end of file diff --git a/open/djus_search.js b/open/djus_search.js new file mode 100644 index 0000000..af2b035 --- /dev/null +++ b/open/djus_search.js @@ -0,0 +1,379 @@ +/* +@header({ + searchable: 1, + filterable: 0, + quickSearch: 1, + title: '网盘资源[搜]', + lang: 'ds' +}) +*/ +import '../lib/htmlParser.js'; +import { Quark, Baidu, UC } from "../lib/pans.js"; + +let siteName = '夸克短剧搜索', siteKey = '', siteType = 0; + +let quarkInfinite = 0; +let quarkOrig = 1; +let quarkTransfer = 1; +let enableProxy = 0; + +let UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0 Safari/537.36"; + +let headers = { + "User-Agent": UA, + "Accept": "application/json, text/plain, */*", + "Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8" +}; + +let proxyurl = 'http://127.0.0.1:2525/proxy?url='; +let downThreads = '20'; + +let defaultImg = `https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/网盘搜索.png`; +let quark_img = `https://cnb.cool/zhyadc/YsBox/-/git/raw/main/images/icon_cookie/夸克.png`; + +let host = 'https://api.uuuka.com'; +let maxPages = 5; + +function safeJSONParse(str, defaultValue = {}) { + if (!str || typeof str === 'object') return str || defaultValue; + try { + return JSON.parse(str); + } catch { + return defaultValue; + } +} + +async function request(url, options = {}) { + const reqHeaders = { ...headers, ...options.headers }; + let postType = reqHeaders['Content-Type']?.includes('json') ? 'json' : + reqHeaders['Content-Type']?.includes('form') ? 'form' : ''; + + try { + const response = await req(url, { + method: options.method || 'GET', + headers: reqHeaders, + data: options.data, + postType: postType, + timeout: options.timeout || 15000 + }); + return response?.content || response?.data || response; + } catch { + return null; + } +} + +function mapResolution(res) { + const map = { + 'low': '流畅', 'normal': '标清', 'high': '高清', 'super': '超清', + '4k': '4K', '2k': '2K', 'hdr': 'HDR', 'dolby_vision': 'HDR', + 'M3U8_AUTO_480': '480P', 'M3U8_AUTO_720': '720P', 'M3U8_AUTO_1080': '1080P', + 'M3U8_AUTO_2K': '2K', 'M3U8_AUTO_4K': '4K' + }; + return map[res] || res; +} + +function buildSearchUrl(wd, page) { + return `${host}/api/search?keyword=${encodeURIComponent(wd)}&content_type=post&page=${page}&limit=100`; +} + +async function loadRemoteConfig(cfgUrl) { + try { + const res = await request(cfgUrl, { timeout: 10000 }); + if (res) { + const remoteCfg = safeJSONParse(res); + if (remoteCfg.quark_cookie?.length > 10) Quark.cookie = remoteCfg.quark_cookie; + if (remoteCfg.baidu_cookie?.length > 10) Baidu.cookie = remoteCfg.baidu_cookie; + if (remoteCfg.uc_cookie?.length > 10) UC.cookie = remoteCfg.uc_cookie; + if (remoteCfg.uc_token?.length > 10) UC.token = remoteCfg.uc_token; + if (remoteCfg.threads) downThreads = remoteCfg.threads; + if (remoteCfg.enableProxy !== undefined) enableProxy = remoteCfg.enableProxy; + if (remoteCfg.infinite !== undefined) quarkInfinite = remoteCfg.infinite; + if (remoteCfg.quark_original !== undefined) quarkOrig = remoteCfg.quark_original; + if (remoteCfg.quark_transfer !== undefined) quarkTransfer = remoteCfg.quark_transfer; + return true; + } + } catch (e) {} + return false; +} + +async function init(cfg) { + siteName = cfg.skey?.split('_')[1] || cfg.skey || siteName; + siteKey = cfg.skey; + siteType = cfg.stype; + + let ext = cfg.ext || cfg; + if (typeof ext === 'string') ext = decodeURIComponent(ext); + if (typeof ext === 'string') { + await loadRemoteConfig(ext); + } + +} + + +function home() { + return JSON.stringify({ + class: [ + { type_id: 'search', type_name: '这是一个搜索源' } + ], + }); +} + +async function homeVod() { + return await category('search', 1, null, null); +} + +async function category(tid, pg, filter, ext) { + if (tid === 'search') { + return JSON.stringify({ + list: [{ + vod_id: 'only_search', + vod_name: '🔍 网盘资源搜索', + vod_pic: defaultImg, + }] + }); + } + return JSON.stringify({ list: [] }); +} + +async function detail(id) { + let parts = id.split('|'); + let shareUrl = parts[0]; + let vod_name = parts[1] || '网盘资源'; + + let play_from = []; + let play_url = []; + let play_pic = []; + + if (shareUrl && shareUrl.startsWith('http')) { + let counters = { '夸克': 1, '百度': 1, '优汐': 1 }; + + if (/\.quark|pan\.quark/.test(shareUrl)) { + let shareData = Quark.getShareData(shareUrl); + if (shareData) { + let files = await Quark.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.pdir_fid || '', v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`夸克#${counters['夸克']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } + else if (/\.baidu|pan\.baidu/.test(shareUrl)) { + let shareData = Baidu.getShareData(shareUrl); + if (shareData) { + let files = await Baidu.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + let info = { surl: shareData.surl, pwd: shareData.pwd || '' }; + return `${v.name}${size}$${[v.path, v.uk, v.shareid, v.fsid, JSON.stringify(info)].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`百度#${counters['百度']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } + else if (/\.uc|drive\.uc/.test(shareUrl)) { + let shareData = UC.getShareData(shareUrl); + if (shareData) { + let files = await UC.getFilesByShareUrl(shareData); + if (files?.length) { + let url = files.map(v => { + let size = v.size ? ` 【${formatSize(v.size)}】` : ''; + return `${v.file_name}${size}$${[shareData.shareId, v.stoken, v.fid, v.share_fid_token, v.subtitle?.fid || '', v.subtitle?.share_fid_token || ''].join('*')}`; + }).join('#'); + let imgs = files.map(v => v.thumbnail || v.thumb || v.pic || '').join('#'); + play_from.push(`优汐#${counters['优汐']++}`); + play_url.push(url); + play_pic.push(imgs); + } + } + } + } + + if (play_from.length === 0) { + play_from.push('提示'); + play_url.push(shareUrl ? `分享链接: ${shareUrl}` : `获取分享链接失败`); + play_pic.push(''); + } + + let vod = { + vod_id: id, + vod_name: vod_name, + vod_pic: '', + vod_content: `网盘链接: ${shareUrl}`, + vod_remarks: play_from.length > 0 ? (play_from[0].split('#')[0] + '可播放') : '解析失败', + vod_play_from: play_from.join('$$$'), + vod_play_url: play_url.join('$$$'), + vod_play_pic: play_pic.join('$$$'), + vod_play_pic_ratio: 1.0 + }; + + return JSON.stringify({ list: [vod] }); +} + +async function search(wd, quick, pg) { + let page = pg || 1; + + let apiUrl = buildSearchUrl(wd, page); + let response = await request(apiUrl, { headers: headers, timeout: 15000 }); + let allList = []; + + if (response) { + try { + let data = safeJSONParse(response); + if (data.success && data.data && data.data.items) { + for (let item of data.data.items) { + let title = item.title; + let shareUrl = item.source_link; + + if (!title || !shareUrl) continue; + + let detailId = `${shareUrl}|${title}`; + + allList.push({ + vod_id: detailId, + vod_name: title, + vod_pic: quark_img, + vod_content: `更新时间: ${item.update_time || '未知'}`, + vod_remarks: '夸克网盘' + }); + } + } + } catch (e) {} + } + + let filteredList = allList.filter(item => new RegExp(wd, "i").test(item.vod_name)); + + return JSON.stringify({ + page: page, + pagecount: 999, + limit: filteredList.length, + total: filteredList.length, + list: filteredList + }); +} + +async function play(flag, id, flags) { + let ids = id.split('*'); + let urls = []; + + const addUrl = (name, u) => { + if (u) urls.push(name, proxyUrl(u) + `&thread=${downThreads}`); + }; + + if (flag.startsWith('夸克') && ids.length >= 4) { + let [shareId, stoken, fid, share_fid_token] = ids; + let header = { + 'User-Agent': headers['User-Agent'], + 'origin': 'https://pan.quark.cn', + 'referer': 'https://pan.quark.cn/', + 'Cookie': Quark.cookie + }; + + if (quarkInfinite == 1) { + const tokenUrls = await Quark.getUrl(shareId, stoken, fid, share_fid_token); + tokenUrls?.forEach(item => { if (item?.url) addUrl("无限" + (item.name || ''), item.url); }); + } + + let shouldTransfer = quarkTransfer; + if (quarkInfinite != 1 && quarkOrig != 1 && quarkTransfer != 1) { + shouldTransfer = 1; + } + + if (shouldTransfer == 1) { + if (quarkOrig == 1) { + let down = await Quark.getDownload(shareId, stoken, fid, share_fid_token, true); + if (down && down.error) { + const errorMsg = down.message || '未知错误'; + return JSON.stringify({ parse: 0, msg: `夸克: ${errorMsg}`, header: header }); + } + if (down?.download_url) addUrl("原画", down.download_url); + } + + let transcoding = await Quark.getLiveTranscoding(shareId, stoken, fid, share_fid_token); + if (transcoding?.length) { + transcoding.filter(t => t.accessable).forEach(t => { + if (t?.video_info?.url) { + addUrl(mapResolution(t.resolution), t.video_info.url); + } + }); + } + } + + if (urls.length) { + return JSON.stringify({ parse: 0, url: urls, header: header }); + } + } + + if (flag.startsWith('百度') && ids.length >= 5) { + let [path, uk, shareid, fsid, shareDataStr] = ids; + let shareData = safeJSONParse(shareDataStr); + + let original = await Baidu.getAppShareUrl(path, uk, shareid, fsid, shareData); + if (original && !original.error) { + addUrl("原画", original); + } else { + const errorMsg = original?.message || '获取链接失败,请检查Cookie是否有效'; + return JSON.stringify({ parse: 0, msg: `百度: ${errorMsg}`, header: headers }); + } + + if (urls.length) { + return JSON.stringify({ + parse: 0, + url: urls, + header: { "User-Agent": 'netdisk;P2SP;2.2.91.136;android-android;' } + }); + } + } + + if (flag.startsWith('优汐') && ids.length >= 4) { + let [shareId, stoken, fid, share_fid_token] = ids; + + let down = await UC.getDownload(shareId, stoken, fid, share_fid_token, true); + if (down && down.error) { + const errorMsg = down.message || '未知错误'; + return JSON.stringify({ parse: 0, msg: `优汐: ${errorMsg}`, header: headers }); + } + if (down?.length) { + down.forEach(item => { if (item?.url) addUrl(mapResolution(item.name), item.url); }); + } + + if (urls.length) { + return JSON.stringify({ parse: 0, url: urls }); + } + } + + return JSON.stringify({ parse: 0, msg: `解析分享链接失败, 分享链接可能已失效`, header: headers }); +} + +function formatSize(bytes) { + let size = typeof bytes === 'string' ? parseInt(bytes, 10) : bytes; + if (!size || isNaN(size) || size <= 0) return '0 B'; + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + let i = 0; + while (size >= 1024 && i < units.length - 1) { size /= 1024; i++; } + return size.toFixed(2) + ' ' + units[i]; +} + +function proxyUrl(url) { + if (!url) return ''; + if (enableProxy && proxyurl) return proxyurl + encodeURIComponent(url); + return url; +} + +async function proxy(params) { + let url = params.url; + return [302, '', '', { 'Location': url }]; +} + +export function __jsEvalReturn() { + return { init, home, homeVod, category, detail, play, search, proxy }; +} \ No newline at end of file diff --git a/open/duanju_juhe.js b/open/duanju_juhe.js new file mode 100644 index 0000000..2e25b78 --- /dev/null +++ b/open/duanju_juhe.js @@ -0,0 +1,2178 @@ +/* +@header({ + searchable: 1, + filterable: 1, + quickSearch: 1, + title: '聚合短剧', + lang: 'cat' +}) +*/ + + +import { Crypto as CryptoJS } from 'assets://js/lib/cat.js'; + +let shuaCache = []; +let siteName = '聚合短剧'; +let xingya_headers = {}; +let niuniu_headers = {}; +let niuniu_token = ''; +let niuniu_access_token = ''; +let hema_headers = {}; + +// 分类排除规则 +const cate_remove = ['分类排除', '软鸭','碎片', '锦鲤', '番茄', '甜圈']; + +const aggConfig = { + keys: 'd3dGiJc651gSQ8w1', + charMap: { + '+': 'P', '/': 'X', '0': 'M', '1': 'U', '2': 'l', '3': 'E', '4': 'r', '5': 'Y', '6': 'W', '7': 'b', '8': 'd', '9': 'J', + 'A': '9', 'B': 's', 'C': 'a', 'D': 'I', 'E': '0', 'F': 'o', 'G': 'y', 'H': '_', 'I': 'H', 'J': 'G', 'K': 'i', 'L': 't', + 'M': 'g', 'N': 'N', 'O': 'A', 'P': '8', 'Q': 'F', 'R': 'k', 'S': '3', 'T': 'h', 'U': 'f', 'V': 'R', 'W': 'q', 'X': 'C', + 'Y': '4', 'Z': 'p', 'a': 'm', 'b': 'B', 'c': 'O', 'd': 'u', 'e': 'c', 'f': '6', 'g': 'K', 'h': 'x', 'i': '5', 'j': 'T', + 'k': '-', 'l': '2', 'm': 'z', 'n': 'S', 'o': 'Z', 'p': '1', 'q': 'V', 'r': 'v', 's': 'j', 't': 'Q', 'u': '7', 'v': 'D', + 'w': 'w', 'x': 'n', 'y': 'L', 'z': 'e' + }, + headers: { + default: { + 'User-Agent': 'okhttp/5.1.0', + 'Content-Type': 'application/json' + }, + niuniu: {'Cache-Control':'no-cache','Content-Type':'application/json;charset=UTF-8','User-Agent':'okhttp/4.12.0'}, + baidu: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'User-Agent': 'Dalvik/2.1.0 (Linux; U; Android 9; 22081212C Build/PQ3B.190801.002) Talos/1.8.13 SP-engine/3.47.0 bd_dvt/1 baiduboxapp/15.21.0.10 (Baidu; P1 9)' + }, + hema: { + 'datas': 'e5f22c6e2c82fe001738cb9ce4696eab0556d064a55aef402e0fbe6b29a083f6538e4567de38e67de2071a49d9751526bfba45314e1fd4702b11c76ab9a3b5f873262854ba66e6715ed51364dbc6ee62c7180e047fcbcdbfd49874fc8f28674b16d90ca71a02de76c70598e0b75e647c37c2c19287e49be5f2a259d727dfc4df3d28802388bf3c356576b342e17e30a2ab74859263dba4d1c8eba79990d22d60d60927fdacb2addf2f0eaadd8887585ca2eb87f603faf0c207dda18cf67dc25b2199d303baff9e6605b3314a7d2631f62864f48619daceb9452f2b7b0667773553741856df030cca68af3c57810f983d452bb428ef5fc32206aef4865ae06c629bee7f5135547304acc7ef4e7c6df887308f2e79c493fd2ee03488722861b5bb51b09cb8911dfc92c288d94e601c066d2f9d612ad2c8d4eeb4920b1d44aff3e13fd75229b857f64925df1cf12f75a00d438c422ec1726462b915903f1dd1f4bb7cdf82cc15a6d507f80c789903e710f39a62aef073f3f93a6c681e75d295428aa290d7e98f82e7e9ad6e2b23d9086dfe8c63c5d8550b13fd61a77291473a8bdd43c7c2639f264be69d9d07f0585de4342a399275a64e7d1d4400b8ed4421a2f289f622e40cdd1cfc916a0b9ce747c924ac33e32d24b91ed5d64772d6ad6896412f52724006eabf12aaecfd6e81dad432c7b3800bbf793a1c375e3e7b4fb3b097724b5fc88a8c9bcf3dbc10cbdb252965', + 'content-type': 'text/plain' + } + } +}; + +// ==================== URL配置集中管理 ==================== +const rule = { + 百度: { + host: 'https://mbd.baidu.com', + detailHost: 'https://sv.baidu.com', + list: '/feedapi/v1/videoserver/playlets/list?service=bdbox', + search: '/feedapi/v1/videoserver/playlets/search?service=bdbox', + detail: '/haokan/ui-video/playlet/rec/detail?log=vhk&tn=1020970b&ctn=1008350n&blur=1', + play: '/appui/api?cmd=video/relate&log=vhk&tn=1020970b&ctn=1008350n&blur=1' + }, + 甜圈: { + host: 'https://mov.cenguigui.cn', + list: '/duanju/api.php?classname', + detail: '/duanju/api.php?book_id', + search: '/duanju/api.php?name' + }, + 锦鲤: { + host: 'https://api.jinlidj.com', + search: '/api/search', + detail: '/api/detail' + }, + 番茄: { + host: 'https://reading.snssdk.com', + list: '/reading/bookapi/bookmall/cell/change/v', + detail: 'https://fqgo.52dns.cc/catalog', + search: 'https://fqgo.52dns.cc/search' + }, + 星芽: { + host: 'https://app.whjzjx.cn', + list: '/cloud/v2/theater/home_page?theater_class_id', + detail: '/v2/theater_parent/detail', + search: '/v3/search', + login: 'https://u.shytkjgs.com/user/v1/account/login' + }, + 西饭: { + host: 'https://xifan-api-cn.youlishipin.com', + list: '/xifan/drama/portalPage', + detail: '/xifan/drama/getDuanjuInfo', + search: '/xifan/search/getSearchList' + }, + 软鸭: { + host: 'https://api.xingzhige.com', + list: '/API/playlet', + search: '/API/playlet' + }, + 七猫: { + host: 'https://api-store.qmplaylet.com', + list: '/api/v1/playlet/index', + detail: 'https://api-read.qmplaylet.com/player/api/v1/playlet/info', + search: '/api/v1/playlet/search' + }, + 牛牛: { + host: 'https://new.tianjinzhitongdaohe.com', + list: '/api/v1/app/screen/screenMovie', + detail: '/api/v1/app/play/movieDetails', + search: '/api/v1/app/search/searchMovie', + desc: '/api/v1/app/play/movieDesc', + visitor: '/api/v1/app/user/visitorInfo', + login: 'https://csj-sp.csjdeveloper.com/csj_sp/api/v1/user/login?siteid=5627189', + detail2: 'https://csj-sp.csjdeveloper.com/csj_sp/api/v1/shortplay/detail?siteid=5627189', + unlock: 'https://csj-sp.csjdeveloper.com/csj_sp/api/v1/pay/ad_unlock?siteid=5627189' + }, + 围观: { + host: 'https://api.drama.9ddm.com', + list: '/drama/home/shortVideoTags?version_code=1500&os_type=1', + detail: '/drama/home/shortVideoDetail?version_code=1500&os_type=1', + search: '/drama/home/search?version_code=1500&os_type=1' + }, + 碎片: { + host: 'https://free-api.bighotwind.cc', + list: '/papaya/papaya-api/theater/tags', + detail: '/papaya/papaya-api/videos/info', + search: '/papaya/papaya-api/videos/page' + }, + 河马: { + host: 'https://freevideo.zqqds.cn', + list: '/free-video-portal/portal/1121', + detail: '/free-video-portal/portal/1131', + episode: '/free-video-portal/portal/1132', + play: '/free-video-portal/portal/1133', + search: '/free-video-portal/portal/1803' + } +}; + +const platformList = [ + { name: '锦鲤短剧', id: '锦鲤' }, + { name: '番茄短剧', id: '番茄' }, + { name: '星芽短剧', id: '星芽' }, + { name: '西饭短剧', id: '西饭' }, + { name: '七猫短剧', id: '七猫' }, + { name: '甜圈短剧', id: '甜圈' }, + { name: '牛牛短剧', id: '牛牛' }, + { name: '百度短剧', id: '百度' }, + { name: '围观短剧', id: '围观' }, + { name: '软鸭短剧', id: '软鸭' }, + { name: '碎片剧场', id: '碎片' }, + { name: '河马短剧', id: '河马' } +]; + +const ruleFilterDef = { + 百度: { area: '新剧' }, + 甜圈: { area: '逆袭' }, + 锦鲤: { area: '' }, + 番茄: { area: 'videoseries_hot' }, + 星芽: { area: '1' }, + 西饭: { area: '' }, + 软鸭: { area: '战神' }, + 七猫: { area: '0' }, + 牛牛: { area: '现言' }, + 围观: { area: '' }, + 碎片: { area: '' }, + 河马: { area: '308' } +}; + +const filterOptions = { + "甜圈": [{ + "key": "area", + "name": "剧情", + "value": [ + {"n": "逆袭", "v": "逆袭"}, + {"n": "霸总", "v": "霸总"}, + {"n": "现代言情", "v": "现代言情"}, + {"n": "打脸虐渣", "v": "打脸虐渣"}, + {"n": "豪门恩怨", "v": "豪门恩怨"}, + {"n": "神豪", "v": "神豪"}, + {"n": "马甲", "v": "马甲"}, + {"n": "都市日常", "v": "都市日常"}, + {"n": "战神归来", "v": "战神归来"}, + {"n": "小人物", "v": "小人物"}, + {"n": "女性成长", "v": "女性成长"}, + {"n": "大女主", "v": "大女主"}, + {"n": "穿越", "v": "穿越"}, + {"n": "都市修仙", "v": "都市修仙"}, + {"n": "强者回归", "v": "强者回归"}, + {"n": "亲情", "v": "亲情"}, + {"n": "古装", "v": "古装"}, + {"n": "重生", "v": "重生"}, + {"n": "闪婚", "v": "闪婚"}, + {"n": "赘婿逆袭", "v": "赘婿逆袭"}, + {"n": "虐恋", "v": "虐恋"}, + {"n": "追妻", "v": "追妻"}, + {"n": "天下无敌", "v": "天下无敌"}, + {"n": "家庭伦理", "v": "家庭伦理"}, + {"n": "萌宝", "v": "萌宝"}, + {"n": "古风权谋", "v": "古风权谋"}, + {"n": "职场", "v": "职场"}, + {"n": "奇幻脑洞", "v": "奇幻脑洞"}, + {"n": "异能", "v": "异能"}, + {"n": "无敌神医", "v": "无敌神医"}, + {"n": "古风言情", "v": "古风言情"}, + {"n": "传承觉醒", "v": "传承觉醒"}, + {"n": "现言甜宠", "v": "现言甜宠"}, + {"n": "奇幻爱情", "v": "奇幻爱情"}, + {"n": "乡村", "v": "乡村"}, + {"n": "历史古代", "v": "历史古代"}, + {"n": "王妃", "v": "王妃"}, + {"n": "高手下山", "v": "高手下山"}, + {"n": "娱乐圈", "v": "娱乐圈"}, + {"n": "强强联合", "v": "强强联合"}, + {"n": "破镜重圆", "v": "破镜重圆"}, + {"n": "暗恋成真", "v": "暗恋成真"}, + {"n": "民国", "v": "民国"}, + {"n": "欢喜冤家", "v": "欢喜冤家"}, + {"n": "系统", "v": "系统"}, + {"n": "真假千金", "v": "真假千金"}, + {"n": "龙王", "v": "龙王"}, + {"n": "校园", "v": "校园"}, + {"n": "穿书", "v": "穿书"}, + {"n": "女帝", "v": "女帝"}, + {"n": "团宠", "v": "团宠"}, + {"n": "年代爱情", "v": "年代爱情"}, + {"n": "玄幻仙侠", "v": "玄幻仙侠"}, + {"n": "青梅竹马", "v": "青梅竹马"}, + {"n": "悬疑推理", "v": "悬疑推理"}, + {"n": "皇后", "v": "皇后"}, + {"n": "替身", "v": "替身"}, + {"n": "大叔", "v": "大叔"}, + {"n": "喜剧", "v": "喜剧"}, + {"n": "剧情", "v": "剧情"} + ] + }], + "锦鲤": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""}, + {"n": "情感关系", "v": "1"}, + {"n": "成长逆袭", "v": "2"}, + {"n": "奇幻异能", "v": "3"}, + {"n": "战斗热血", "v": "4"}, + {"n": "伦理现实", "v": "5"}, + {"n": "时空穿越", "v": "6"}, + {"n": "权谋身份", "v": "7"} + ] + }], + "番茄": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "热剧", "v": "videoseries_hot"}, + {"n": "新剧", "v": "firstonlinetime_new"}, + {"n": "逆袭", "v": "cate_739"}, + {"n": "总裁", "v": "cate_29"}, + {"n": "现言", "v": "cate_3"}, + {"n": "打脸", "v": "cate_1051"}, + {"n": "马甲", "v": "cate_266"}, + {"n": "豪门", "v": "cate_1053"}, + {"n": "都市", "v": "cate_261"}, + {"n": "神豪", "v": "cate_20"} + ] + }], + "星芽": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "剧场", "v": "1"}, + {"n": "热播剧", "v": "2"}, + {"n": "会员专享", "v": "8"}, + {"n": "星选好剧", "v": "7"}, + {"n": "新剧", "v": "3"}, + {"n": "阳光剧场", "v": "5"} + ] + }], + "西饭": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""}, + {"n": "都市", "v": "68@都市"}, + {"n": "青春", "v": "68@青春"}, + {"n": "现代言情", "v": "81@现代言情"}, + {"n": "豪门", "v": "81@豪门"}, + {"n": "大女主", "v": "80@大女主"}, + {"n": "逆袭", "v": "79@逆袭"}, + {"n": "打脸虐渣", "v": "79@打脸虐渣"}, + {"n": "穿越", "v": "81@穿越"} + ] + }], + "软鸭": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""}, + {"n": "战神", "v": "战神"}, + {"n": "逆袭", "v": "逆袭"}, + {"n": "霸总", "v": "霸总"}, + {"n": "神豪", "v": "神豪"}, + {"n": "都市", "v": "都市"}, + {"n": "玄幻", "v": "玄幻"}, + {"n": "言情", "v": "言情"} + ] + }], + "七猫": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""}, + {"n": "推荐", "v": "0"}, + {"n": "新剧", "v": "-1"}, + {"n": "都市情感", "v": "1273"}, + {"n": "古装", "v": "1272"}, + {"n": "都市", "v": "571"}, + {"n": "玄幻仙侠", "v": "1286"}, + {"n": "奇幻", "v": "570"}, + {"n": "乡村", "v": "590"}, + {"n": "民国", "v": "573"}, + {"n": "年代", "v": "572"}, + {"n": "青春校园", "v": "1288"}, + {"n": "武侠", "v": "371"}, + {"n": "科幻", "v": "594"}, + {"n": "末世", "v": "556"}, + {"n": "二次元", "v": "1289"}, + {"n": "逆袭", "v": "400"}, + {"n": "穿越", "v": "373"}, + {"n": "复仇", "v": "795"}, + {"n": "系统", "v": "787"}, + {"n": "权谋", "v": "790"}, + {"n": "重生", "v": "784"}, + {"n": "女性成长", "v": "1294"}, + {"n": "打脸虐渣", "v": "716"}, + {"n": "闪婚", "v": "480"}, + {"n": "强者回归", "v": "402"}, + {"n": "追妻火葬场", "v": "715"}, + {"n": "家庭", "v": "670"}, + {"n": "马甲", "v": "558"}, + {"n": "职场", "v": "724"}, + {"n": "宫斗", "v": "343"}, + {"n": "高手下山", "v": "1299"}, + {"n": "娱乐明星", "v": "1295"}, + {"n": "异能", "v": "727"}, + {"n": "宅斗", "v": "342"}, + {"n": "替身", "v": "712"}, + {"n": "穿书", "v": "338"}, + {"n": "商战", "v": "723"}, + {"n": "种田经商", "v": "1291"}, + {"n": "伦理", "v": "1293"}, + {"n": "社会话题", "v": "1290"}, + {"n": "致富", "v": "492"}, + {"n": "偷听心声", "v": "1258"}, + {"n": "脑洞", "v": "526"}, + {"n": "豪门总裁", "v": "624"}, + {"n": "萌宝", "v": "356"}, + {"n": "战神", "v": "527"}, + {"n": "真假千金", "v": "812"}, + {"n": "赘婿", "v": "36"}, + {"n": "神医", "v": "1269"}, + {"n": "神豪", "v": "37"}, + {"n": "小人物", "v": "1296"}, + {"n": "团宠", "v": "545"}, + {"n": "欢喜冤家", "v": "464"}, + {"n": "女帝", "v": "617"}, + {"n": "银发", "v": "1297"}, + {"n": "兵王", "v": "28"}, + {"n": "虐恋", "v": "16"}, + {"n": "甜宠", "v": "21"}, + {"n": "悬疑", "v": "27"}, + {"n": "搞笑", "v": "793"}, + {"n": "灵异", "v": "1287"} + ] + }], + "牛牛": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""}, + {"n": "现言", "v": "现言"}, + {"n": "古言", "v": "古言"}, + {"n": "历史", "v": "历史"}, + {"n": "都市", "v": "都市"}, + {"n": "活动", "v": "活动"}, + {"n": "逆袭", "v": "逆袭"}, + {"n": "豪门", "v": "豪门"}, + {"n": "现代言情", "v": "现代言情"}, + {"n": "战神", "v": "战神"}, + {"n": "甜宠", "v": "甜宠"}, + {"n": "穿越", "v": "穿越"}, + {"n": "古装", "v": "古装"}, + {"n": "虐心", "v": "虐心"}, + {"n": "神医", "v": "神医"}, + {"n": "赘婿", "v": "赘婿"}, + {"n": "亲情", "v": "亲情"}, + {"n": "复仇", "v": "复仇"}, + {"n": "玄幻", "v": "玄幻"}, + {"n": "古代言情", "v": "古代言情"}, + {"n": "热血", "v": "热血"}, + {"n": "动作", "v": "动作"}, + {"n": "喜剧", "v": "喜剧"}, + {"n": "悬疑", "v": "悬疑"}, + {"n": "军事", "v": "军事"}, + {"n": "二次元", "v": "二次元"}, + {"n": "未来", "v": "未来"}, + {"n": "快速穿越", "v": "快速穿越"}, + {"n": "烧脑", "v": "烧脑"}, + {"n": "治愈", "v": "治愈"}, + {"n": "其他剧情", "v": "其他剧情"} + ] + }], + "百度": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "新剧", "v": "新剧"}, + {"n": "限时免费", "v": "限时免费"}, + {"n": "精选", "v": "精选"}, + {"n": "独播", "v": "独播"}, + {"n": "全部", "v": "全部题材"}, + {"n": "神医", "v": "神医"}, + {"n": "连续剧", "v": "连续剧"}, + {"n": "都市", "v": "都市"}, + {"n": "现代言情", "v": "现代言情"}, + {"n": "异能", "v": "异能"}, + {"n": "逆袭", "v": "逆袭"}, + {"n": "甜宠", "v": "甜宠"}, + {"n": "总裁", "v": "总裁"}, + {"n": "萌宝", "v": "萌宝"}, + {"n": "战神", "v": "战神"}, + {"n": "宫斗宅斗", "v": "宫斗宅斗"}, + {"n": "神豪", "v": "神豪"}, + {"n": "虐恋", "v": "虐恋"}, + {"n": "闪婚", "v": "闪婚"}, + {"n": "玄幻", "v": "玄幻"}, + {"n": "穿越重生", "v": "穿越重生"}, + {"n": "年代", "v": "年代"}, + {"n": "家庭伦理", "v": "家庭伦理"}, + {"n": "古代言情", "v": "古代言情"}, + {"n": "武侠武打", "v": "武侠武打"}, + {"n": "赘婿", "v": "赘婿"}, + {"n": "单元剧", "v": "单元剧"}, + {"n": "青春校园", "v": "青春校园"}, + {"n": "历史架空", "v": "历史架空"}, + {"n": "王妃", "v": "王妃"}, + {"n": "鉴宝", "v": "鉴宝"}, + {"n": "科幻", "v": "科幻"}, + {"n": "军旅战争", "v": "军旅战争"}, + {"n": "种田", "v": "种田"} + ] + }], + "围观": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""} + ] + }], + "碎片": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "全部", "v": ""} + ] + }], + "河马": [{ + "key": "area", + "name": "分类", + "value": [ + {"n": "推荐", "v": "308"}, + {"n": "新剧", "v": "309"}, + {"n": "逆袭", "v": "310"}, + {"n": "恋爱", "v": "311"}, + {"n": "强者回归", "v": "312"}, + {"n": "豪门恩怨", "v": "313"}, + {"n": "古装", "v": "314"}, + {"n": "重生", "v": "315"}, + {"n": "萌宝", "v": "316"}, + {"n": "复仇", "v": "317"}, + {"n": "神医", "v": "318"}, + {"n": "高手下山", "v": "319"}, + {"n": "超能悬疑", "v": "320"}, + {"n": "传承觉醒", "v": "321"}, + {"n": "神豪", "v": "322"}, + {"n": "民国", "v": "323"} + ] + }] +}; + +// 河马分类标签映射 +const hemaTagIds = { + "308": "", + "309": "", + "310": "417,473,474,464", + "311": "462,466", + "312": "476", + "313": "585,616", + "314": "444,468", + "315": "417,439,464,465", + "316": "589", + "317": "416,439,463,465", + "318": "438", + "319": "417,474,464", + "320": "439,442,443,445,465,470", + "321": "417,473,474,464", + "322": "472,475,585", + "323": "590" +}; + +// ==================== 初始化 ==================== +async function init(cfg) { + console.log(`【${siteName}】初始化开始`); + + // 星芽登录 + try { + const loginData = { device: '24250683a3bdb3f118dff25ba4b1cba1a' }; + const response = await request(rule.星芽.login, { + method: 'POST', + headers: { 'User-Agent': 'okhttp/4.10.0', 'platform': '1', 'Content-Type': 'application/json' }, + data: loginData + }); + + const res = JSON.parse(response); + const token = res?.data?.token || res?.data?.data?.token || res?.token || res?.result?.token || res?.access_token; + + if (token) { + xingya_headers = { ...aggConfig.headers.default, authorization: token }; + console.log(`【${siteName}】星芽登录成功`); + } else { + xingya_headers = aggConfig.headers.default; + } + } catch (e) { + console.log(`【${siteName}】星芽登录失败: ${e.message}`); + xingya_headers = aggConfig.headers.default; + } + + // 牛牛登录 + try { + // 获取visitor token + let tkhtml = await request(rule.牛牛.host + rule.牛牛.visitor, { + headers: { + "deviceid": "aa11fc54-ba9c-3980-add5-447d3fa5b939", + "token": "", + "User-Agent": "okhttp/4.12.0", + "client": "app", + "devicetype": "Android" + } + }); + + let tkRes = JSON.parse(tkhtml); + niuniu_token = tkRes.data.token; + console.log("牛牛token:", niuniu_token); + + // 获取access_token + let t = String(Math.floor(new Date().getTime() / 1000)); + let body = `ac=wifi&os=Android&vod_version=1.10.21.6-tob&os_version=9&type=1&clientVersion=v5.2.5&uuid=Y4WNZ3SAWK7MAJMH7CXCDHJ4VMPVFRZQTBSIA4XTYO4AWEUHIK6Q01&resolution=1280*2618&openudid=889edced38f1069b&dt=Pixel%204&sha1=46121F77CE2FCAD3DBC3B9EC8A24908C1A8AD6D9&os_api=28&install_id=1549688030634536&device_brand=google&sdk_version=1.1.3.0&package_name=com.niuniu.ztdh.app&siteid=5627189&dev_log_aid=667431&oaid=×tamp=${t}`; + + let nonce = "VX1KKGtoBDCi1fB1"; + let Signature = t + nonce + body; + let signature = hmacSHA256(Signature, 'aceaa47f96b4875d446b2e1d97e03bbb'); + let encbdoy = aesEncryptECB(body, 'dafdb3d2a5c343d6'); + + let loginpost = await request(rule.牛牛.login, { + headers: { + 'X-Salt': '786774955F', + 'X-Nonce': nonce, + 'X-Timestamp': t, + 'X-Signature': signature, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data: encbdoy, + method: "POST" + }); + let logindata = aesDecryptECB(loginpost, 'dafdb3d2a5c343d6'); + let accesstoken = JSON.parse(logindata); + niuniu_access_token = accesstoken.data.access_token; + console.log(`【${siteName}】牛牛登录成功`); + + niuniu_headers = { + ...aggConfig.headers.niuniu, + "token": niuniu_token, + "deviceid": "aa11fc54-ba9c-3980-add5-447d3fa5b939" + }; + + } catch (e) { + console.log(`【${siteName}】牛牛登录失败: ${e.message}`); + niuniu_headers = aggConfig.headers.niuniu; + } + + // 河马初始化 + try { + hema_headers = { ...aggConfig.headers.hema }; + console.log(`【${siteName}】河马初始化成功`); + } catch (e) { + console.log(`【${siteName}】河马初始化失败: ${e.message}`); + hema_headers = aggConfig.headers.hema; + } + + return true; +} + +// ==================== 首页分类 ==================== +function home(filter) { + const platForms = getPlatList(); + + const classes = platForms.map(item => ({ + type_name: item.name, + type_id: item.id, + type_flag: '[CFS][SUBSITE2][FILTERBAR]' + })); + + const filters = {}; + platForms.forEach(item => { + if (filterOptions[item.id]) filters[item.id] = filterOptions[item.id]; + }); + + return JSON.stringify({ class: classes, filters: filters }); +} + +// ==================== 首页推荐 ==================== +async function homeVod() { + const platForms = getPlatList(); + + const randomPlat = platForms[Math.floor(Math.random() * platForms.length)]; + const randomArea = ruleFilterDef[randomPlat.id]?.area || ''; + + const categoryResult = await category(randomPlat.id, 1, { area: randomArea }, {}); + const categoryList = JSON.parse(categoryResult).list || []; + + return JSON.stringify({ + list: categoryList + }); +} + +// ==================== 分类列表 ==================== +async function category(tid, pg, filter, extend) { + const page = pg || 1; + extend = extend || {}; + + const platformItem = platformList.find(p => p.id === tid); + if (platformItem && isSkipPlat(platformItem)) { + return JSON.stringify({ list: [], page, pagecount: 1, limit: 0, total: 0 }); + } + + const searchKeyword = extend?.custom; + if (searchKeyword) { + return await cfs(tid, searchKeyword, pg); + } + + const platRule = rule[tid]; + const area = filter?.area || extend?.area || ruleFilterDef[tid]?.area || ''; + const videos = []; + + switch (tid) { + case '百度': { + let sub = ["新剧","限时免费","精选","独播"].includes(area) ? area : "新剧"; + let tcsub = area === "全部" || area === "全部题材" ? "" : area; + let t = Math.floor(Date.now() / 1000); + let version = await md5(t + "v2"); + + let postData = { + 'data': { + "data": { + "extRequest": { "flow_tabid": "13" }, + "from": "feed", + "page": "channel_video_landing", + "pd": "feed", + "refreshIndex": parseInt(page), + "cursor": "", + "theme": "", + "timestamp": t, + "version": version, + "themes": [ + { "kind": "综合", "names": [sub] }, + { "kind": "题材", "names": [tcsub] } + ] + } + } + }; + + let html = await request(`${platRule.host}${platRule.list}`, { + method: 'POST', + headers: aggConfig.headers.baidu, + data: postData + }); + let res = JSON.parse(html); + let items = res.data?.items || []; + items.slice(0, 20).forEach(it => { + videos.push({ + vod_id: `百度@${it.collId}`, + vod_name: it.title || '未知短剧', + vod_pic: it.img || '', + vod_remarks: '百度短剧 | ' + (it.updateStatus || "更新中"), + vod_content: it.description || '' + }); + }); + break; + } + + case '甜圈': { + const url = `${platRule.host}${platRule.list}=${area}&offset=${page}`; + const response = await request(url, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + (res.data || []).forEach(it => { + videos.push({ + vod_id: `甜圈@${it.book_id}`, + vod_name: it.title || '未知标题', + vod_pic: it.cover || '', + vod_remarks: '甜圈短剧 | ' + (it.copyright || ''), + vod_content: it.desc || '' + }); + }); + break; + } + + case '锦鲤': { + const postData = { page, limit: 24, type_id: area, year: '', keyword: '' }; + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + data: postData + }); + const res = JSON.parse(response); + (res.data?.list || []).forEach(item => { + videos.push({ + vod_id: `锦鲤@${item.vod_id}`, + vod_name: item.vod_name || '', + vod_pic: item.vod_pic || '', + vod_remarks: '锦鲤短剧 | ' + (item.vod_total ? `${item.vod_total}集` : ''), + vod_content: item.vod_tag || '' + }); + }); + break; + } + + case '番茄': { + const sessionId = new Date().toISOString().slice(0,16).replace(/-|T:/g,''); + let url = `${platRule.host}${platRule.list}?change_type=0&selected_items=${area}&tab_type=8&cell_id=6952850996422770718&version_tag=video_feed_refactor&device_id=1423244030195267&aid=1967&app_name=novelapp&ssmix=a&session_id=${sessionId}`; + if (page > 1) url += `&offset=${(page-1)*12}`; + + const response = await request(url, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + let items = res?.data?.cell_view?.cell_data || res?.search_tabs?.find(t => t.title === '短剧' && t.data)?.data || res?.data || []; + + items.forEach(item => { + const videoData = item.video_data?.[0] || item; + videos.push({ + vod_id: `番茄@${videoData.series_id || videoData.book_id || videoData.id || ''}`, + vod_name: videoData.title || '未知短剧', + vod_pic: videoData.cover || videoData.horiz_cover || '', + vod_remarks: '番茄短剧 | ' + (videoData.sub_title || videoData.rec_text || ''), + vod_content: videoData.abstract || '' + }); + }); + break; + } + + case '星芽': { + const url = `${platRule.host}${platRule.list}=${area}&type=1&class2_ids=0&page_num=${page}&page_size=24`; + const response = await request(url, { headers: xingya_headers }); + const res = JSON.parse(response); + (res.data?.list || []).forEach(it => { + videos.push({ + vod_id: `星芽@${it.theater.id}`, + vod_name: it.theater.title || '', + vod_pic: it.theater.cover_url || '', + vod_remarks: '星芽短剧 | ' + (it.theater.total ? `${it.theater.total}集` : ''), + vod_content: `播放量:${it.theater.play_amount_str || 0}` + }); + }); + break; + } + + case '西饭': { + const [typeId, typeName] = area.split('@'); + const ts = Math.floor(Date.now() / 1000); + const url = `${platRule.host}${platRule.list}?reqType=aggregationPage&offset=${(page-1)*30}&categoryId=${typeId}&quickEngineVersion=-1&scene=&categoryNames=${encodeURIComponent(typeName)}&categoryVersion=1&density=1.5&pageID=page_theater&version=2001001&androidVersionCode=28&requestId=${ts}aa498144140ef297&appId=drama&teenMode=false&userBaseMode=false&session=eyJpbmZvIjp7InVpZCI6IiIsInJ0IjoiMTc0MDY1ODI5NCIsInVuIjoiT1BHXzFlZGQ5OTZhNjQ3ZTQ1MjU4Nzc1MTE2YzFkNzViN2QwIiwiZnQiOiIxNzQwNjU4Mjk0In19&feedssession=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1dHlwIjowLCJidWlkIjoxNjMzOTY4MTI2MTQ4NjQxNTM2LCJhdWQiOiJkcmFtYSIsInZlciI6MiwicmF0IjoxNzQwNjU4Mjk0LCJ1bm0iOiJPUEdfMWVkZDk5NmE2NDdlNDUyNTg3NzUxMTY2YzFkNzViN2QwIiwiZXhwIjoxNzQxMjYzMDk0LCJkYyI6Imd6cXkifQ.JS3QY6ER0P2cQSxAE_OGKSMIWNAMsYUZ3mJTnEpf-Rc`; + + const response = await request(url, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + + (res.result?.elements || []).forEach(soup => { + (soup.contents || []).forEach(vod => { + const dj = vod.duanjuVo || {}; + videos.push({ + vod_id: `西饭@${dj.duanjuId}#${dj.source}`, + vod_name: dj.title || '', + vod_pic: dj.coverImageUrl || '', + vod_remarks: '西饭短剧 | ' + (dj.total ? `${dj.total}集` : ''), + vod_content: dj.desc || '' + }); + }); + }); + break; + } + + case '软鸭': { + const url = `${platRule.host}${platRule.list}/?keyword=${encodeURIComponent(area)}&page=${page}`; + const response = await request(url, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + (res.data || []).forEach(item => { + const purl = `${item.title}@${item.cover}@${item.author}@${item.type}@${item.desc}@${item.book_id}`; + videos.push({ + vod_id: `软鸭@${encodeURIComponent(purl)}`, + vod_name: item.title || '', + vod_pic: item.cover || '', + vod_remarks: '软鸭短剧 | ' + (item.type || ''), + vod_content: item.author || '' + }); + }); + break; + } + + case '七猫': { + let signStr = `operation=1playlet_privacy=1tag_id=${area}${aggConfig.keys}`; + const sign = await md5(signStr); + const url = `${platRule.host}${platRule.list}?tag_id=${area}&playlet_privacy=1&operation=1&sign=${sign}`; + const headers = await getQiMaoHeaders(); + + const response = await request(url, { method: 'GET', headers }); + const res = JSON.parse(response); + (res.data?.list || []).forEach(item => { + videos.push({ + vod_id: `七猫@${encodeURIComponent(item.playlet_id)}`, + vod_name: item.title || '', + vod_pic: item.image_link || '', + vod_remarks: '七猫短剧 | ' + (item.total_episode_num ? `${item.total_episode_num}集` : ''), + vod_content: item.tags || '' + }); + }); + break; + } + + case '牛牛': { + const postData = { + condition: { classify: area, typeId: 'S1' }, + pageNum: page, + pageSize: 24 + }; + const response = await request(`${platRule.host}${platRule.list}`, { + method: 'POST', + headers: niuniu_headers, + data: postData + }); + const res = JSON.parse(response); + (res.data?.records || []).forEach(item => { + videos.push({ + vod_id: `牛牛@${item.id}`, + vod_name: item.name || '', + vod_pic: item.cover || '', + vod_remarks: '牛牛短剧 | ' + (item.totalEpisode ? `${item.totalEpisode}集` : ''), + vod_content: item.description || '' + }); + }); + break; + } + + case '围观': { + const postData = { + audience: "全部受众", + page: page, + pageSize: 30, + searchWord: "", + subject: "全部主题" + }; + + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + headers: aggConfig.headers.default, + data: postData + }); + const res = JSON.parse(response); + if (res.code === 200 && res.data) { + (res.data || []).forEach(it => { + videos.push({ + vod_id: `围观@${it.oneId}`, + vod_name: it.title || '未知短剧', + vod_pic: it.vertPoster || it.horizonPoster || '', + vod_remarks: '围观短剧 | ' + `集数:${it.episodeCount || 0}`, + vod_content: it.description || '' + }); + }); + } + break; + } + + case '碎片': { + const token = await getSuiPianToken(); + const headers = { ...aggConfig.headers.default, 'Authorization': token }; + const url = `${platRule.host}${platRule.search}?type=5&tagId=&pageNum=${page}&pageSize=24`; + + const response = await request(url, { headers }); + const res = JSON.parse(response); + + if (res && res.list && res.list.length > 0) { + (res.list || []).forEach(it => { + videos.push({ + vod_id: `碎片@${it.itemId}@${it.videoCode}`, + vod_name: it.title || '未知剧名', + vod_pic: it.imageKey ? `https://free-api.bighotwind.cc/papaya/papaya-file/files/download/${it.imageKey}/${it.imageName || 'cover.jpg'}` : 'https://t8.baidu.com/it/u=615012979,225344800&fm=193', + vod_remarks: '碎片剧场 | ' + (it.episodesMax ? `${it.episodesMax}集` : '') + (it.hitShowNum ? ` 播放:${it.hitShowNum}` : ''), + vod_content: it.content || it.description || '' + }); + }); + } + break; + } + + case '河马': { + try { + const sub = area || '308'; + const tagIds = hemaTagIds[sub] || ''; + + const bodys = JSON.stringify({ + "recSwitch": true, + "channelId": sub, + "tagIds": tagIds, + "cnxhFlag": page - 1, + "playListFlag": true, + "watchRecords": ["41000103722_572752006"] + }); + + const body = hemaEncrypt(bodys); + + const response = await request(`${platRule.host}${platRule.list}`, { + method: 'POST', + headers: hema_headers, + data: body + }); + + const res = JSON.parse(response); + const dehtml = res.data; + + if (dehtml) { + const hmdata = hemaDecrypt(dehtml); + if (hmdata && hmdata !== '{}') { + const hmlist = JSON.parse(hmdata).columnData || []; + hmlist.forEach(videoDataArray => { + (videoDataArray.videoData || []).forEach(video => { + videos.push({ + vod_id: `河马@${video.bookId}`, + vod_name: video.bookName || '', + vod_pic: video.coverWap || video.coverCutWap, + vod_remarks: `河马短剧 | 更新${video.updateNum || 0}集`, + vod_content: video.introduction || '', + vod_actor: video.author || '', + extra: { + bookId: video.bookId, + chapterId: video.chapterId, + chapterMin: video.updateNum, + chapterMax: video.chapterIndex + } + }); + }); + }); + } + } + } catch (e) { + console.log(`河马分类失败: ${e.message}`); + } + break; + } + } + + return JSON.stringify({ + list: videos, + page: page, + pagecount: page + 1, + limit: videos.length, + total: videos.length * (page + 1) + }); +} + +// ==================== 详情 ==================== +async function detail(id) { + + const parts = id.split('@'); + const platform = parts[0]; + const did = parts.slice(1).join('@'); + const platRule = rule[platform]; + let vod = {}; + + switch (platform) { + case '百度': { + const postData = { playlet_id: did, vid: "undefined" }; + let html = await request(`${platRule.detailHost}${platRule.detail}`, { + method: 'POST', + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + data: postData + }); + let res = JSON.parse(html); + let dthtml = res.data || {}; + let vids = dthtml.vid_list || []; + let playArr = vids.map((vid, index) => `第${index+1}集$${vid}`); + + vod = { + vod_id: id, + vod_name: dthtml.playlet_title || '未知短剧', + vod_pic: dthtml.playlet_poster || '', + vod_content: `热度值:${dthtml.hot_value||0}\n题材:${dthtml.tag_text||''}\n集数:${dthtml.episodes_num||0}\n简介:${dthtml.description||''}`, + vod_remarks: `共${vids.length||0}集`, + vod_director: dthtml.tag_text || '', + vod_year: dthtml.create_time || '', + vod_play_from: "百度短剧", + vod_play_url: playArr.join('#') + }; + break; + } + + case '甜圈': { + const response = await request(`${platRule.host}${platRule.detail}=${did}`); + const res = JSON.parse(response); + vod = { + vod_id: id, + vod_name: res.book_name || '未知标题', + vod_type: res.category || '', + vod_pic: res.book_pic || '', + vod_remarks: res.duration || '', + vod_year: `更新时间:${res.time || '未知'}`, + vod_actor: res.author || '', + vod_content: res.desc || '', + vod_play_from: '甜圈短剧', + vod_play_url: (res.data || []).map(item => `${item.title || '第1集'}$${item.video_id || item.id || ''}`).join('#') + }; + break; + } + + case '锦鲤': { + const response = await request(`${platRule.host}${platRule.detail}/${did}`); + const res = JSON.parse(response); + const list = res.data || {}; + const playUrls = list.player ? Object.keys(list.player).map(key => `${key}$${list.player[key]}`) : []; + vod = { + vod_id: list.vod_id || id, + vod_name: list.vod_name || '暂无名称', + vod_type: list.vod_class || '暂无类型', + vod_pic: list.vod_pic || '暂无图片', + vod_remarks: list.vod_remarks || '暂无备注', + vod_year: list.vod_year || '暂无年份', + vod_area: list.vod_area || '暂无地区', + vod_actor: list.vod_actor || '暂无演员', + vod_director: list.vod_director || '暂无导演', + vod_content: list.vod_blurb || '暂无剧情', + vod_play_from: '锦鲤短剧', + vod_play_url: playUrls.join('#') + }; + break; + } + + case '番茄': { + const response = await request(`${platRule.detail}?book_id=${did}`); + const res = JSON.parse(response); + const bookInfo = res.data?.book_info || {}; + const playList = (res.data?.item_data_list || []).map(item => `${item.title}$${item.item_id}`).join('#'); + vod = { + vod_id: bookInfo.book_id || id, + vod_name: bookInfo.book_name || '', + vod_type: bookInfo.tags || '', + vod_year: bookInfo.create_time || '', + vod_pic: bookInfo.thumb_url || bookInfo.audio_thumb_uri || '', + vod_content: bookInfo.abstract || bookInfo.book_abstract_v2 || '', + vod_remarks: bookInfo.sub_info || `更新至${res.data?.item_data_list?.length || 0}集`, + vod_play_from: '番茄短剧', + vod_play_url: playList + }; + break; + } + + case '星芽': { + const detailUrl = `${platRule.host}${platRule.detail}?theater_parent_id=${did}`; + const response = await request(detailUrl, { headers: xingya_headers }); + const res = JSON.parse(response); + + if (res.code === 'ok' && res.data) { + const data = res.data; + const playUrls = []; + if (data.theaters && Array.isArray(data.theaters)) { + data.theaters.forEach((item) => { + if (item.son_video_url) { + const epTitle = `第${item.num}集`; + playUrls.push(`${epTitle}$${item.son_video_url}`); + } + }); + } + + vod = { + vod_id: id, + vod_name: data.title || '未知剧名', + vod_type: data.class_two?.map(c => c.class_name).join(',') || '', + vod_pic: data.cover_url || '', + vod_area: `收藏${data.collect_number || 0}`, + vod_actor: `点赞${data.like_num || 0}`, + vod_director: `评分${data.score || 0}`, + vod_remarks: data.is_over === 2 ? '连载中' : '已完结', + vod_content: data.introduction || data.desc || '', + vod_play_from: '星芽短剧', + vod_play_url: playUrls.length > 0 ? playUrls.join('#') : '暂无播放地址$0' + }; + } + break; + } + + case '西饭': { + const [duanjuId, source] = did.split('#'); + const url = `${platRule.host}${platRule.detail}?duanjuId=${duanjuId}&source=${source}`; + const response = await request(url, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + const data = res.result || {}; + const playUrls = (data.episodeList || []).map(ep => `${ep.index}$${ep.playUrl}`).join('#'); + + vod = { + vod_id: id, + vod_name: data.title || '', + vod_pic: data.coverImageUrl || '', + vod_content: data.desc || '未知', + vod_remarks: data.updateStatus === 'over' ? `${data.total || 0}集 已完结` : `更新${data.total || 0}集`, + vod_play_from: '西饭短剧', + vod_play_url: playUrls + }; + break; + } + + case '软鸭': { + const didDecoded = decodeURIComponent(did); + const parts = didDecoded.split('@'); + const title = parts[0] || ''; + const img = parts[1] || ''; + const author = parts[2] || ''; + const type = parts[3] || ''; + const desc = parts[4] || ''; + const book_id = parts[5] || did.split('@')[5] || ''; + + const detailUrl = `${platRule.host}${platRule.list}/?book_id=${book_id}`; + const response = await request(detailUrl, { headers: aggConfig.headers.default }); + const res = JSON.parse(response); + const playUrls = (res.data?.video_list || []).map(ep => `${ep.title}$${ep.video_id}`).join('#'); + + vod = { + vod_id: id, + vod_name: title || '', + vod_pic: img || '', + vod_actor: author || '', + vod_remarks: type || '', + vod_content: desc || '', + vod_play_from: '软鸭短剧', + vod_play_url: playUrls + }; + break; + } + + case '七猫': { + const didDecoded = decodeURIComponent(did); + const sign = await md5(`playlet_id=${didDecoded}${aggConfig.keys}`); + const url = `${platRule.detail}?playlet_id=${didDecoded}&sign=${sign}`; + const headers = await getQiMaoHeaders(); + + const response = await request(url, { method: 'GET', headers }); + const res = JSON.parse(response); + const data = res.data || {}; + + vod = { + vod_id: id, + vod_name: data.title || '未知标题', + vod_pic: data.image_link || '未知图片', + vod_actor: '', + vod_remarks: `${data.tags || ''} ${data.total_episode_num || 0}集`, + vod_content: data.intro || '未知剧情', + vod_play_from: '七猫短剧', + vod_play_url: (data.play_list || []).map(it => `${it.sort}$${it.video_url}`).join('#') + }; + break; + } + + case '牛牛': { + const descData = await request(`${platRule.host}${platRule.desc}`, { + method: 'POST', + headers: niuniu_headers, + data: { id: did, typeId: 'S1' } + }); + const descRes = JSON.parse(descData); + const descInfo = descRes.data || {}; + + const listData = await request(`${platRule.host}${platRule.detail}`, { + method: 'POST', + headers: niuniu_headers, + data: { id: did, source: 0, typeId: 'S1', userId: '546932' } + }); + const listRes = JSON.parse(listData); + const listInfo = listRes.data || {}; + + let playUrls = ''; + if (listInfo.url && listInfo.episodeList && listInfo.episodeList.length > 0) { + playUrls = (listInfo.episodeList || []).map(ep => `${ep.episode}$${did}+${ep.id}`).join('#'); + } else if (listInfo.thirdPlayId) { + let thirdPlayId = listInfo.thirdPlayId; + + let data1 = "not_include=0&lock_free=1&type=1&clientVersion=v5.2.5&uuid=6IDYUSASPQY5BBVACWQW3LLTPV4V7DE26UOCX5TZTVUGX4VUJNXQ01&resolution=1080*2320&openudid=82f4175d577a2939&dt=22021211RC&os_api=31&install_id=1496879012031075&sdk_version=1.1.3.0&siteid=5627189&dev_log_aid=667431&oaid=abec0dfff623201b×tamp=1752498494&direction=0&ac=mobile&os=Android&vod_version=1.10.21.6-tob&os_version=12&count=1&index=1&shortplay_id="+thirdPlayId+"&sha1=46121F77CE2FCAD3DBC3B9EC8A24908C1A8AD6D9&device_brand=Redmi&package_name=com.niuniu.ztdh.app"; + + try { + let html1 = await niuniuPost(rule.牛牛.detail2, data1, "1"); + if (html1 && html1.data && html1.data.episode_right_list) { + playUrls = html1.data.episode_right_list.map(it => { + let lockType = it.lock_type || 'free'; + return `第${it.index}集$${it.index}+${lockType}+${thirdPlayId}`; + }).join('#'); + } + } catch (e) { + console.log("获取加密剧集失败:", e.message); + } + } + + vod = { + vod_id: id, + vod_name: descInfo.name || listInfo.name || '未知名称', + vod_pic: descInfo.cover || listInfo.cover || '', + vod_content: `类型:${descInfo.classify || ''}\n评分:${descInfo.score || ''}\n简介:${descInfo.introduce || ''}`, + vod_remarks: `共${descInfo.totalEpisode || listInfo.totalEpisode || 0}集`, + vod_play_from: '牛牛短剧', + vod_play_url: playUrls || '暂无播放地址$0' + }; + break; + } + + case '围观': { + const response = await request(`${platRule.host}${platRule.detail}&oneId=${did}&page=1&pageSize=1000`, { + headers: aggConfig.headers.default + }); + const res = JSON.parse(response); + if (res.code === 200 && res.data) { + const data = res.data || []; + const firstEpisode = data[0] || {}; + + vod = { + vod_id: id, + vod_name: firstEpisode.title || '', + vod_pic: firstEpisode.vertPoster || firstEpisode.horizonPoster || '', + vod_remarks: `共${data.length || 0}集`, + vod_content: `播放量:${firstEpisode.viewCount || 0} 收藏:${firstEpisode.collectionCount || 0} 评论:${firstEpisode.commentCount || 0}`, + vod_play_from: '围观短剧', + vod_play_url: data.map(ep => { + let playSetting = ep.playSetting || ep.videoClarityList || []; + try { + if (typeof playSetting === 'string') { + playSetting = JSON.parse(playSetting); + } + } catch (e) {} + + // 确保是数组 + if (!Array.isArray(playSetting)) playSetting = []; + + // 清晰度优先级:1080P > 720P > 480P + const url = ( + playSetting.find(item => item.name === '1080P')?.url || + playSetting.find(item => item.name === '720P')?.url || + playSetting.find(item => item.name === '480P')?.url || + '' + ); + + const title = `第${ep.playOrder || 1}集`; + return `${title}$${url}`; + }).filter(ep => ep.split('$')[1]).join('#') + }; + } + break; + } + + case '碎片': { + const [itemId, videoCode] = did.split('@'); + const token = await getSuiPianToken(); + const headers = { ...aggConfig.headers.default, 'Authorization': token }; + const url = `${platRule.host}${platRule.detail}?videoCode=${videoCode}&itemId=${itemId}`; + + const response = await request(url, { headers }); + const res = JSON.parse(response); + const data = res.data || res; + const playUrls = (data.episodesList || []).map(episode => { + let title = `第${episode.episodes || 1}集`; + if (episode.resolutionList?.length) { + episode.resolutionList.sort((a, b) => b.resolution - a.resolution); + let best = episode.resolutionList[0]; + return `${title}$${`https://free-api.bighotwind.cc/papaya/papaya-file/files/download/${best.fileKey}/${best.fileName}`}`; + } + return null; + }).filter(item => item).join('#'); + + vod = { + vod_id: id, + vod_name: data.title || '', + vod_pic: `https://free-api.bighotwind.cc/papaya/papaya-file/files/download/${data.imageKey || ''}/${data.imageName || ''}`, + vod_remarks: `共${data.episodesMax || 0}集`, + vod_content: data.content || data.description || `播放量:${data.hitShowNum || 0} 点赞:${data.likeNum || 0}`, + vod_play_from: '碎片剧场', + vod_play_url: playUrls + }; + break; + } + + case '河马': { + const bookId = did; + + // 获取详情 + const body = hemaEncrypt(JSON.stringify({ "bookId": bookId })); + const detailResponse = await request(`${platRule.host}${platRule.detail}`, { + method: 'POST', + headers: hema_headers, + data: body + }); + + const detailRes = JSON.parse(detailResponse); + const detailHtml = detailRes.data; + const postdata = hemaDecrypt(detailHtml); + const videoInfo = JSON.parse(postdata).videoInfo || {}; + + // 获取剧集 + const episodeBody = hemaEncrypt(JSON.stringify({ + "bookId": bookId, + "chapterMin": videoInfo.updateNum || 0, + "chapterMax": videoInfo.chapterIndex || 0 + })); + + const episodeResponse = await request(`${platRule.host}${platRule.episode}`, { + method: 'POST', + headers: hema_headers, + data: episodeBody + }); + + const episodeRes = JSON.parse(episodeResponse); + const episodeHtml = episodeRes.data; + const playdata = hemaDecrypt(episodeHtml); + const chapterList = JSON.parse(playdata).chapterList || []; + // 构建播放列表 + const playUrls = chapterList.map(item => + `${item.chapterName}$${item.chapterId}++${item.chapterIndex}++${bookId}` + ).join('#'); + + // 处理数组字段 + const vodType = Array.isArray(videoInfo.bookTags) ? videoInfo.bookTags.join(',') : (videoInfo.bookTags || ''); + const vodActor = Array.isArray(videoInfo.protagonist) ? videoInfo.protagonist.join(',') : (videoInfo.protagonist || ''); + + vod = { + vod_id: id, + vod_name: videoInfo.bookName || '未知剧名', + vod_type: vodType, + vod_pic: videoInfo.coverWap, + vod_remarks: videoInfo.finishStatusCn || `更新至${videoInfo.updateNum || 0}集`, + vod_content: videoInfo.introduction || '暂无简介', + vod_actor: vodActor, + vod_director: videoInfo.author || '', + vod_year: videoInfo.updateTime || '', + vod_play_from: '河马短剧', + vod_play_url: playUrls || '暂无播放地址$0' + }; + break; + } + } + + return JSON.stringify({ list: [vod] }); +} + +// ==================== 播放 ==================== +async function play(flag, id, flags) { + if (/百度/.test(flag)) { + const postData = { method: "post", vid: id }; + let html = await request(`${rule.百度.detailHost}${rule.百度.play}`, { + method: 'POST', + headers: aggConfig.headers.baidu, + data: postData + }); + + let res = JSON.parse(html); + let json = res["video/relate"]?.data?.cur_video; + + if (!json?.clarityUrl) { + return JSON.stringify({ parse: 0, url: id }); + } + + let urls = json.clarityUrl + .filter(item => item.url && item.title) + .map(item => ({ + title: item.title, + url: item.url, + order: { '蓝光': 1, '超清': 2, '标清': 3 }[item.title] || 999 + })) + .sort((a, b) => a.order - b.order) + .flatMap(item => [item.title, item.url]); + + return JSON.stringify({ + parse: urls.length > 0 ? 0 : 1, + url: urls.length > 0 ? urls : id, + header: { + 'User-Agent': aggConfig.headers.baidu['User-Agent'], + 'Referer': 'https://mbd.baidu.com/' + } + }); + } + + if (/甜圈/.test(flag)) { + return JSON.stringify({ parse: 0, url: `https://mov.cenguigui.cn/duanju/api.php?video_id=${id}&type=mp4` }); + } + + if (/锦鲤/.test(flag)) { + try { + const response = await request(`${id}&auto=1`); + const match = response.match(/let data\s*=\s*({[^;]*});/); + if (match) { + const data = JSON.parse(match[1]); + return JSON.stringify({ parse: 0, url: data.url }); + } + } catch (error) {} + } + + if (/番茄/.test(flag)) { + const response = await request(`https://fqgo.52dns.cc/video?item_ids=${id}`, { + headers: aggConfig.headers.default + }); + const res = JSON.parse(response); + if (res.data?.[id]) { + const videoModel = JSON.parse(res.data[id].video_model); + const url = videoModel?.video_list?.video_1 ? base64Decode(videoModel.video_list.video_1.main_url) : ''; + return JSON.stringify({ parse: 0, url }); + } + } + + if (/软鸭/.test(flag)) { + const response = await request(`${rule.软鸭.host}/API/playlet/?video_id=${id}&quality=original`, { + headers: aggConfig.headers.default + }); + const res = JSON.parse(response); + return JSON.stringify({ parse: 0, url: res.data?.video?.url || '' }); + } + + if (/牛牛/.test(flag)) { + const inputArr = id.split('+'); + + if (inputArr.length === 2) { + var match = inputArr[0].match(/\d+/); + var ep = match ? match[0] : ""; + var videoId = inputArr[1]; + + var postData = { + id: videoId, + source: 0, + typeId: "S1", + userId: "546932", + episodeId: ep + }; + + var response = await request(`${rule.牛牛.host}/api/v1/app/play/movieDetails`, { + method: 'POST', + headers: niuniu_headers, + data: postData + }); + + var result = JSON.parse(response); + if (result.code == 200 && result.data && result.data.url) { + return JSON.stringify({ parse: 0, url: result.data.url }); + } else { + return JSON.stringify({ parse: 0, url: id }); + } + } + else if (inputArr.length === 3) { + var index = inputArr[0]; + var lock_type = inputArr[1]; + var thirdPlayId = inputArr[2]; + + if (lock_type === "free") { + let frdata = "not_include=0&lock_free=1&type=1&clientVersion=v5.2.5&uuid=6IDYUSASPQY5BBVACWQW3LLTPV4V7DE26UOCX5TZTVUGX4VUJNXQ01&resolution=1080*2320&openudid=82f4175d577a2939&dt=22021211RC&os_api=31&install_id=1496879012031075&sdk_version=1.1.3.0&siteid=5627189&dev_log_aid=667431&oaid=abec0dfff623201b×tamp=1752498494&direction=0&ac=mobile&os=Android&vod_version=1.10.21.6-tob&os_version=12&count=1&index=1&shortplay_id="+thirdPlayId+"&sha1=46121F77CE2FCAD3DBC3B9EC8A24908C1A8AD6D9&device_brand=Redmi&package_name=com.niuniu.ztdh.app"; + let frhtml = await niuniuPost(rule.牛牛.detail2, frdata, index); + if (frhtml && frhtml.data && frhtml.data.list && frhtml.data.list[0]) { + let url = base64Decode(frhtml.data.list[0].video_model.video_list.video_1.main_url); + return JSON.stringify({ parse: 0, url }); + } + } else { + let unlockData = "ac=mobile&os=Android&vod_version=1.10.21.6-tob&os_version=12&lock_ad=3&lock_free=3&type=1&clientVersion=v5.2.5&uuid=6IDYUSASPQY5BBVACWQW3LLTPV4V7DE26UOCX5TZTVUGX4VUJNXQ01&resolution=1080*2320&openudid=82f4175d577a2939&shortplay_id=" + thirdPlayId + "&dt=22021211RC&sha1=46121F77CE2FCAD3DBC3B9EC8A24908C1A8AD6D9&lock_index=21&os_api=31&install_id=1496879012031075&device_brand=Redmi&sdk_version=1.1.3.0&package_name=com.niuniu.ztdh.app&siteid=5627189&dev_log_aid=667431&oaid=abec0dfff623201b×tamp=1752498493"; + + await niuniuPost(rule.牛牛.unlock, unlockData, index); + + let udata = "not_include=0&lock_free=1&type=1&clientVersion=v5.2.5&uuid=6IDYUSASPQY5BBVACWQW3LLTPV4V7DE26UOCX5TZTVUGX4VUJNXQ01&resolution=1080*2320&openudid=82f4175d577a2939&dt=22021211RC&os_api=31&install_id=1496879012031075&sdk_version=1.1.3.0&siteid=5627189&dev_log_aid=667431&oaid=abec0dfff623201b×tamp=1752498494&direction=0&ac=mobile&os=Android&vod_version=1.10.21.6-tob&os_version=12&count=1&index=1&shortplay_id="+thirdPlayId+"&sha1=46121F77CE2FCAD3DBC3B9EC8A24908C1A8AD6D9&device_brand=Redmi&package_name=com.niuniu.ztdh.app"; + let unhtml = await niuniuPost(rule.牛牛.detail2, udata, index); + if (unhtml && unhtml.data && unhtml.data.list && unhtml.data.list[0]) { + let url = base64Decode(unhtml.data.list[0].video_model.video_list.video_1.main_url); + return JSON.stringify({ parse: 0, url }); + } + } + } + + return JSON.stringify({ parse: 0, url: id }); + } + + if (/围观/.test(flag)) { + try { + let playSetting = typeof id === 'string' ? JSON.parse(id) : id; + let urls = []; + if (playSetting.super) urls.push("超清", playSetting.super); + if (playSetting.high) urls.push("高清", playSetting.high); + if (playSetting.normal) urls.push("流畅", playSetting.normal); + return JSON.stringify({ parse: 0, url: urls.length ? urls : id }); + } catch (e) { + return JSON.stringify({ parse: 0, url: id }); + } + } + + if (/星芽/.test(flag)) { + return JSON.stringify({ parse: 0, url: id }); + } + + if (/河马/.test(flag)) { + try { + let arr = id.split("++"); + let chapterId = arr[0]; + let index = arr[1]; + let bookId = arr[2]; + + let fsbody = JSON.stringify({ + "bookId": bookId, + "chapterId": chapterId, + "unClockType": "pay", + "confirmPay": 2, + "autoPayFlag": true, + "omap": { + "channelName": "精选", + "logId": "17a6500357709bb2547e1e122b438cfc", + "originName": "书城", + "recId": "bigdata_rec", + "scene": "nsc_727", + "sceneId": "dzmf_video_sc_reco", + "strategyId": "g6y6b5sq" + } + }); + + let fsbodyEnc = hemaEncrypt(fsbody); + + let response = await request(rule.河马.host + rule.河马.play, { + method: 'POST', + headers: hema_headers, + data: fsbodyEnc + }); + + let res = JSON.parse(response); + let fshtml = res.data; + if (fshtml) { + let fsdata = hemaDecrypt(fshtml); + if (fsdata && fsdata !== '{}') { + let parsed = JSON.parse(fsdata); + let type = parsed.chaptersPayType; + + if (type == '免费') { + let data = parsed.chapterInfo || []; + let url = data[0].content.m3u8720p || []; + if (data.length > 0 && data[0].content.m3u8720p) { + return JSON.stringify({ parse: 0, url: url }); + } + } + } + } + + let playurl = "https://api.cenguigui.cn/api/duanju/hema.php?book_id=" + bookId + "&video_id=" + chapterId + "&type=mp4"; + return JSON.stringify({ parse: 0, url: playurl + '#isVideo=true#' }); + + } catch (e) { + console.log(`河马播放失败: ${e.message}`); + return JSON.stringify({ parse: 0, url: id }); + } + } + + return JSON.stringify({ parse: 0, url: id }); +} + +// ==================== 搜索 ==================== +async function cfs(siteId, wd, pg) { + const page = pg || 1; + const searchLimit = 20; + const searchTimeout = 6000; + let results = []; + + const platformItem = platformList.find(p => p.id === siteId); + if (platformItem && isSkipPlat(platformItem)) { + return JSON.stringify({ list: [], page, pagecount: page + 1, limit: 0, total: 0 }); + } + + const platRule = rule[siteId]; + + switch (siteId) { + case '百度': { + let innerData = { + query: wd, + page: page, + attribute: ["title"], + fe_page_type: "search", + extra: { + tab_id: "216", + flow_tabid: "13", + shortplay_source: "feed", + from: "feed", + tab_type: "搜索", + sub_template: "playlet_search_result" + } + }; + + let postData = { 'data': { "data": innerData } }; + let html = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + headers: aggConfig.headers.baidu, + data: postData, + timeout: searchTimeout + }); + + let res = JSON.parse(html); + let data = res.data?.itemList || []; + results = data.map(it => ({ + vod_id: `百度@${it.nid?.split("_")[1] || ''}`, + vod_name: it.title || '未知短剧', + vod_pic: it.img || '', + vod_remarks: '百度短剧 | ' + (it.collNum || "搜索短剧"), + vod_content: it.description || '' + })); + break; + } + + case '甜圈': { + const url = `${platRule.host}${platRule.search}=${encodeURIComponent(wd)}&offset=${page}`; + const response = await request(url, { headers: aggConfig.headers.default, timeout: searchTimeout }); + const res = JSON.parse(response); + results = (res.data || []).map(item => ({ + vod_id: `甜圈@${item.book_id}`, + vod_name: item.title || '未知标题', + vod_pic: item.cover || '', + vod_remarks: '甜圈短剧 | ' + (item.copyright || ''), + vod_content: item.desc || '' + })); + break; + } + + case '锦鲤': { + const postData = { page, limit: searchLimit, type_id: '', year: '', keyword: wd }; + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + data: postData, + timeout: searchTimeout + }); + const res = JSON.parse(response); + results = (res.data?.list || []).map(item => ({ + vod_id: `锦鲤@${item.vod_id}`, + vod_name: item.vod_name || '未知短剧', + vod_pic: item.vod_pic || '', + vod_remarks: '锦鲤短剧 | ' + (item.vod_total ? `${item.vod_total}集` : ''), + vod_content: '' + })); + break; + } + + case '番茄': { + const url = `${platRule.search}?keyword=${encodeURIComponent(wd)}&page=${page}`; + const response = await request(url, { headers: aggConfig.headers.default, timeout: searchTimeout }); + const res = JSON.parse(response); + results = (res.data || []).map(item => ({ + vod_id: `番茄@${item.series_id || ''}`, + vod_name: item.title || '未知标题', + vod_pic: item.cover || '', + vod_remarks: '番茄短剧 | ' + (item.sub_title || ''), + vod_content: '' + })); + break; + } + + case '星芽': { + const postData = { text: wd }; + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + headers: xingya_headers, + data: postData, + timeout: searchTimeout + }); + const res = JSON.parse(response); + results = (res.data?.theater?.search_data || []).map(item => ({ + vod_id: `星芽@${item.id}`, + vod_name: item.title || '', + vod_pic: item.cover_url || '', + vod_remarks: '星芽短剧 | ' + (item.total ? `${item.total}集` : ''), + vod_content: item.introduction || '' + })); + break; + } + + case '西饭': { + const ts = Math.floor(Date.now() / 1000); + const url = `${platRule.host}${platRule.search}?reqType=search&offset=${(page-1)*searchLimit}&keyword=${encodeURIComponent(wd)}&quickEngineVersion=-1&scene=&categoryVersion=1&density=1.5&pageID=page_theater&version=2001001&androidVersionCode=28&requestId=${ts}aa498144140ef297&appId=drama&teenMode=false&userBaseMode=false&session=eyJpbmZvIjp7InVpZCI6IiIsInJ0IjoiMTc0MDY1ODI5NCIsInVuIjoiT1BHXzFlZGQ5OTZhNjQ3ZTQ1MjU4Nzc1MTE2YzFkNzViN2QwIiwiZnQiOiIxNzQwNjU4Mjk0In19&feedssession=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1dHlwIjowLCJidWlkIjoxNjMzOTY4MTI2MTQ4NjQxNTM2LCJhdWQiOiJkcmFtYSIsInZlciI6MiwicmF0IjoxNzQwNjU4Mjk0LCJ1bm0iOiJPUEdfMWVkZDk5NmE2NDdlNDUyNTg3NzUxMTZjMWQ3NWI3ZDAiLCJpZCI6IjNiMzViZmYzYWE0OTgxNDQxNDBlZjI5N2JkMDY5NGNhIiwiZXhwIjoxNzQxMjYzMDk0LCJkYyI6Imd6cXkifQ.JS3QY6ER0P2cQSxAE_OGKSMIWNAMsYUZ3mJTnEpf-Rc`; + + const response = await request(url, { headers: aggConfig.headers.default, timeout: searchTimeout }); + const res = JSON.parse(response); + results = (res.result?.elements || []).map(vod => { + const dj = vod.duanjuVo || {}; + return { + vod_id: `西饭@${dj.duanjuId || ''}#${dj.source || ''}`, + vod_name: dj.title || '未知标题', + vod_pic: dj.coverImageUrl || '', + vod_remarks: '西饭短剧 | ' + (dj.total ? `${dj.total}集` : ''), + vod_content: '' + }; + }); + break; + } + + case '软鸭': { + const url = `${platRule.host}${platRule.search}/?keyword=${encodeURIComponent(wd)}&page=${page}`; + const response = await request(url, { headers: aggConfig.headers.default, timeout: searchTimeout }); + const res = JSON.parse(response); + results = (res.data || []).map(item => { + const purl = `${item.title}@${item.cover}@${item.author}@${item.type}@${item.desc}@${item.book_id}`; + return { + vod_id: `软鸭@${encodeURIComponent(purl)}`, + vod_name: item.title || '', + vod_pic: item.cover || '', + vod_remarks: '软鸭短剧 | ' + (item.type || ''), + vod_content: '' + }; + }); + break; + } + + case '七猫': { + let signStr = `operation=2playlet_privacy=1search_word=${wd}${aggConfig.keys}`; + const sign = await md5(signStr); + const url = `${platRule.host}${platRule.search}?search_word=${encodeURIComponent(wd)}&playlet_privacy=1&operation=2&sign=${sign}`; + const headers = await getQiMaoHeaders(); + + const response = await request(url, { method: 'GET', headers, timeout: searchTimeout }); + const res = JSON.parse(response); + results = (res.data?.list || []).map(item => ({ + vod_id: `七猫@${encodeURIComponent(item.playlet_id)}`, + vod_name: item.title || '未知标题', + vod_pic: item.image_link || '', + vod_remarks: '七猫短剧 | ' + (item.tags || '') + ' ' + (item.total_episode_num ? `${item.total_episode_num}集` : ''), + vod_content: '' + })); + break; + } + + case '牛牛': { + const postData = { + condition: { typeId: "S1", value: wd }, + pageNum: page, + pageSize: searchLimit + }; + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + headers: niuniu_headers, + data: postData, + timeout: searchTimeout + }); + const res = JSON.parse(response); + results = (res.data?.records || []).map(item => ({ + vod_id: `牛牛@${item.id}`, + vod_name: item.name || '', + vod_pic: item.cover || '', + vod_remarks: '牛牛短剧 | ' + (item.totalEpisode ? `${item.totalEpisode}集` : ''), + vod_content: '' + })); + break; + } + + case '围观': { + const postData = { + audience: "", + page: page, + pageSize: searchLimit, + searchWord: wd, + subject: "" + }; + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + data: postData, + timeout: searchTimeout + }); + const res = JSON.parse(response); + + if (res.code === 200 && res.data) { + results = (res.data || []).map(it => ({ + vod_id: `围观@${it.oneId || ''}`, + vod_name: it.title || '未知标题', + vod_pic: it.vertPoster || it.horizonPoster || '', + vod_remarks: '围观短剧 | ' + `集数:${it.episodeCount || 0}`, + vod_content: it.description || '' + })); + } + break; + } + + case '碎片': { + try { + const token = await getSuiPianToken(); + const headers = { ...aggConfig.headers.default, 'Authorization': token }; + const url = `${platRule.host}${platRule.search}?type=5&tagId=&pageNum=${page}&pageSize=${searchLimit}&title=${encodeURIComponent(wd)}`; + + const response = await request(url, { headers, timeout: searchTimeout }); + const res = JSON.parse(response); + + results = (res.list || []).map(it => ({ + vod_id: `碎片@${it.itemId || ''}@${it.videoCode || ''}`, + vod_name: it.title || '', + vod_pic: `https://free-api.bighotwind.cc/papaya/papaya-file/files/download/${it.imageKey || ''}/${it.imageName || ''}`, + vod_remarks: '碎片剧场 | ' + (it.episodesMax ? `${it.episodesMax}集` : '') + (it.hitShowNum ? ` 播放:${it.hitShowNum}` : ''), + vod_content: it.content || it.description || '' + })); + } catch (e) { + console.log(`【碎片搜索】失败: ${e.message}`); + } + break; + } + + case '河马': { + try { + const hmbody = JSON.stringify({ + "keyword": wd, + "page": page, + "size": searchLimit + }); + + const response = await request(`${platRule.host}${platRule.search}`, { + method: 'POST', + headers: hema_headers, + data: hemaEncrypt(hmbody), + timeout: searchTimeout + }); + + const res = JSON.parse(response); + const xmres = res.data; + + if (xmres) { + const dexmres = hemaDecrypt(xmres); + if (dexmres && dexmres !== '{}') { + const xmlist = JSON.parse(dexmres).searchVos || []; + + results = xmlist.map(video => ({ + vod_id: `河马@${video.bookId}`, + vod_name: video.bookName || '', + vod_pic: (video.coverWap || '') + '@Referer=', + vod_remarks: `河马短剧 | 共${video.updateNum || 0}集`, + vod_content: video.introduction || '', + extra: { + bookId: video.bookId, + chapterId: video.chapterId, + chapterMin: video.updateNum, + chapterMax: video.chapterIndex + } + })); + } + } + } catch (e) { + console.log(`河马搜索失败: ${e.message}`); + } + break; + } + } + + return JSON.stringify({ + list: results, + page: page, + pagecount: page + 1, + limit: results.length, + total: results.length * (page + 1) + }); +} + +async function search(wd, quick, pg) { + const videos = []; + const page = pg || 1; + + const platForms = getPlatList(); + + const searchPromises = platForms.map(async (platform) => { + const result = await cfs(platform.id, wd, page); + return JSON.parse(result).list || []; + }); + + const searchResults = await Promise.all(searchPromises); + searchResults.forEach(list => videos.push(...list)); + + const filteredResults = videos.filter(item => + (item.vod_name || '').toLowerCase().includes(wd.toLowerCase()) + ); + + return JSON.stringify({ + list: filteredResults, + page: page, + pagecount: page + 1, + limit: filteredResults.length, + total: filteredResults.length * (page + 1) + }); +} + +async function action(action, value) { + if (action === 'shuaPlay') { + return JSON.stringify({ + action: { + actionId: '__detail__', + ids: value, + keep: true, + } + }); + } +} +// ==================== 河马辅助函数 ==================== +function hemaEncrypt(plaintext) { + var keyBytes = CryptoJS.enc.Hex.parse("647a6b6a67667978677368796c677a6d"); + var ivBytes = CryptoJS.enc.Hex.parse("6170697570646f776e65646372797074"); + var encrypted = CryptoJS.AES.encrypt(plaintext, keyBytes, { + iv: ivBytes, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7 + }); + var ciphertext = encrypted.ciphertext.toString(CryptoJS.enc.Hex); + return ciphertext.toUpperCase(); +} + +function hemaDecrypt(word) { + try { + var key = CryptoJS.enc.Hex.parse("647a6b6a67667978677368796c677a6d"); + var iv = CryptoJS.enc.Hex.parse("6170697570646f776e65646372797074"); + let encryptedHexStr = CryptoJS.enc.Hex.parse(word); + let srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr); + let decrypt = CryptoJS.AES.decrypt(srcs, key, { + iv: iv, + mode: CryptoJS.mode.CBC, + padding: CryptoJS.pad.Pkcs7, + }); + let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8); + return decryptedStr; + } catch (e) { + console.log(`河马解密失败: ${e.message}`); + return '{}'; + } +} + +// ==================== 工具函数 ==================== +function isSkipPlat(platformItem) { + return cate_remove.some(word => + new RegExp(word, 'i').test(platformItem.name) || new RegExp(word, 'i').test(platformItem.id) + ); +} + +function getPlatList() { + return platformList.filter(item => !isSkipPlat(item)); +} + +function getRnd(min, max, hexNum, isUpper) { + let r = Math.floor(Math.random() * (max - min + 1)) + min; + if (hexNum) { + r = isUpper ? r.toString(hexNum).toUpperCase() : r.toString(hexNum); + } + return r; +} + +async function md5(str) { + return CryptoJS.MD5(str).toString(CryptoJS.enc.Hex).toLowerCase(); +} + +function base64Encode(text) { + return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(text)); +} + +function base64Decode(text) { + return CryptoJS.enc.Utf8.stringify(CryptoJS.enc.Base64.parse(text)); +} + +function encHex(txt) { + const key = CryptoJS.enc.Utf8.parse("p0sfjw@k&qmewu#w"); + const encrypted = CryptoJS.AES.encrypt(CryptoJS.enc.Utf8.parse(txt), key, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return encrypted.ciphertext.toString(CryptoJS.enc.Hex); +} + +function guid() { + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + let r = Math.random() * 16 | 0; + let v = c == 'x' ? r : (r & 0x3 | 0x8); + return v.toString(16); + }); +} + +async function getAndSign() { + const sessionId = Math.floor(Date.now()).toString(); + let data = { + "static_score": "0.8", + "uuid": "00000000-7fc7-08dc-0000-000000000000", + "device-id": "20250220125449b9b8cac84c2dd3d035c9052a2572f7dd0122edde3cc42a70", + "mac": "", + "sourceuid": "aa7de295aad621a6", + "refresh-type": "0", + "model": "22021211RC", + "wlb-imei": "", + "client-id": "aa7de295aad621a6", + "brand": "Redmi", + "oaid": "", + "oaid-no-cache": "", + "sys-ver": "12", + "trusted-id": "", + "phone-level": "H", + "imei": "", + "wlb-uid": "aa7de295aad621a6", + "session-id": sessionId + }; + + const jsonStr = JSON.stringify(data); + const base64Str = base64Encode(unescape(encodeURIComponent(jsonStr))); + let qmParams = ''; + for (const c of base64Str) qmParams += aggConfig.charMap[c] || c; + const paramsStr = `AUTHORIZATION=app-version=10001application-id=com.duoduo.readchannel=unknownis-white=net-env=5platform=androidqm-params=${qmParams}reg=${aggConfig.keys}`; + const sign = await md5(paramsStr); + return { qmParams, sign }; +} + +async function getHeaderX() { + const { qmParams, sign } = await getAndSign(); + return { + 'net-env': '5', + 'reg': '', + 'channel': 'unknown', + 'is-white': '', + 'platform': 'android', + 'application-id': 'com.duoduo.read', + 'authorization': '', + 'app-version': '10001', + 'user-agent': 'webviewversion/0', + 'qm-params': qmParams, + 'sign': sign + }; +} + +async function getSuiPianToken() { + let openId = (await md5(guid())).substring(0,16); + let api = "https://free-api.bighotwind.cc/papaya/papaya-api/oauth2/uuid"; + let key = encHex(Date.now().toString()); + + const tokenResponse = await request(api, { + method: 'POST', + headers: { 'Content-Type': 'application/json', "key": key }, + data: { "openId": openId } + }); + + const tokenRes = JSON.parse(tokenResponse); + return tokenRes.data?.token; +} + +async function getQiMaoHeaders() { + const { qmParams, sign } = await getAndSign(); + return { + 'net-env': '5', + 'reg': '', + 'channel': 'unknown', + 'is-white': '', + 'platform': 'android', + 'application-id': 'com.duoduo.read', + 'authorization': '', + 'app-version': '10001', + 'user-agent': 'webviewversion/0', + 'qm-params': qmParams, + 'sign': sign, + ...aggConfig.headers.default + }; +} + +// ==================== 加密工具函数 ==================== +function aesEncryptECB(decrypteddata, key) { + let keyCrypto = CryptoJS.enc.Utf8.parse(key); + let dataCrypto = CryptoJS.enc.Utf8.parse(decrypteddata); + let encrypted = CryptoJS.AES.encrypt(dataCrypto, keyCrypto, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return encrypted.toString(); +} + + +function aesDecryptECB(encryptedData, key) { + try { + let keyCrypto = CryptoJS.enc.Utf8.parse(key); + let encryptedCrypto = CryptoJS.enc.Base64.parse(encryptedData); + let decrypted = CryptoJS.AES.decrypt({ + ciphertext: encryptedCrypto + }, keyCrypto, { + mode: CryptoJS.mode.ECB, + padding: CryptoJS.pad.Pkcs7 + }); + return decrypted.toString(CryptoJS.enc.Utf8); + } catch (e) { + console.log(`ECB解密失败: ${e.message}`); + return ''; + } +} + +function hmacSHA256(message, secretKey) { + let hash = CryptoJS.HmacSHA256(message, secretKey); + return hash.toString(CryptoJS.enc.Hex); +} + +async function niuniuPost(url1, data1, index) { + let t10 = String(Math.floor(new Date().getTime() / 1000)); + let X_Nonce = "X9UknYKtLa3DmtjC"; + let body1 = data1.replace(/&lock_free=\d+/, "&lock_free=1") + .replace(/×tamp=\d+/, "×tamp="+t10) + .replace(/&count=\d+/, "&count=1") + .replace(/&index=\d+/, "&index="+index) + .replace(/&lock_ad=\d+/, "&lock_ad=1") + .replace(/&lock_index=\d+/, "&lock_index="+index); + + let body2 = aesEncryptECB(body1, 'ce49b18dd4e0a4d8'); + let body3 = t10 + X_Nonce + body1; + let signature = hmacSHA256(body3, 'aceaa47f96b4875d446b2e1d97e03bbb'); + + let html1 = await request(url1, { + headers: { + 'X-Salt': 'FD8188A8D5', + 'X-Nonce': X_Nonce, + 'X-Timestamp': t10, + 'X-Access-Token': niuniu_access_token, + 'X-Signature': signature, + 'Content-Type': 'application/x-www-form-urlencoded' + }, + data: body2, + method: "POST" + }); + + html1 = aesDecryptECB(html1, 'ce49b18dd4e0a4d8'); + return JSON.parse(html1); +} + +// ==================== 统一请求函数 ==================== + +async function request(url, options = {}) { + try { + console.log(`【${siteName}】${options.method || 'GET'} ${url.split('?')[0]}`); + + let requestConfig = { + method: options.method || 'GET', + headers: { ...aggConfig.headers.default, ...options.headers }, + timeout: options.timeout || 5000 + }; + + if (options.data) { + if (typeof options.data === 'string') { + requestConfig.body = options.data; + } else { + let contentType = requestConfig.headers['Content-Type'] || ''; + + if (contentType.includes('json')) { + requestConfig.body = JSON.stringify(options.data); + } else { + const parts = []; + for (let key in options.data) { + let value = options.data[key]; + if (typeof value === 'object' && value !== null) { + value = JSON.stringify(value); + } + parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + } + requestConfig.body = parts.join('&'); + } + } + } + + const res = await req(url, requestConfig); + return res.content || ''; + } catch (e) { + console.log(`【${siteName}】请求失败: ${e.message}`); + return ''; + } +} + +export function __jsEvalReturn() { + return { + init: init, + home: home, + homeVod: homeVod, + category: category, + detail: detail, + play: play, + search: search + }; +} \ No newline at end of file