Files
sg/open/97_search.js
T
2026-08-14 11:23:32 +02:00

549 lines
20 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
@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 };
}