修复生产环境使用网盘有效性检测报错无法找到模块
This commit is contained in:
+23
-37
@@ -9,9 +9,15 @@ import {
|
||||
setNetdiskCheckInflight,
|
||||
} from './cache';
|
||||
import type { NetdiskCheckPlatform, NetdiskCheckResult } from './types';
|
||||
|
||||
// eslint-disable-next-line no-eval
|
||||
const nodeRequire = eval('require') as NodeRequire;
|
||||
import { check115 } from './vendor/checkers/pan115';
|
||||
import { checkAliyun } from './vendor/checkers/aliyun';
|
||||
import { checkBaidu } from './vendor/checkers/baidu';
|
||||
import { checkCMCC } from './vendor/checkers/cmcc';
|
||||
import { check123 } from './vendor/checkers/pan123';
|
||||
import { checkQuark } from './vendor/checkers/quark';
|
||||
import { checkTianyi } from './vendor/checkers/tianyi';
|
||||
import { checkUC } from './vendor/checkers/uc';
|
||||
import { checkXunlei } from './vendor/checkers/xunlei';
|
||||
|
||||
const RATE_LIMIT_REASON_PATTERNS = [/频率限制/i, /请求过快/i, /rate.?limit/i, /too many/i, /风控/i];
|
||||
|
||||
@@ -21,10 +27,6 @@ type RawCheckerResult = {
|
||||
isRateLimited?: boolean;
|
||||
};
|
||||
|
||||
type CheckerModule = {
|
||||
[key: string]: (url: string) => Promise<RawCheckerResult>;
|
||||
};
|
||||
|
||||
const PLATFORM_PATTERNS: Record<NetdiskCheckPlatform, RegExp[]> = {
|
||||
'115': [/115(?:cdn)?\.com\/s\//i, /anxia\.com\/s\//i],
|
||||
quark: [/pan\.quark\.cn\/s\//i, /pan\.qoark\.cn\/s\//i],
|
||||
@@ -37,37 +39,18 @@ const PLATFORM_PATTERNS: Record<NetdiskCheckPlatform, RegExp[]> = {
|
||||
cmcc: [/yun\.139\.com\/shareweb/i, /caiyun\.139\.com\/m\/i/i],
|
||||
};
|
||||
|
||||
const CHECKER_EXPORTS: Record<NetdiskCheckPlatform, { modulePath: string; exportName: string }> = {
|
||||
'115': { modulePath: '@/lib/pancheck/vendor/checkers/pan115.js', exportName: 'check115' },
|
||||
aliyun: { modulePath: '@/lib/pancheck/vendor/checkers/aliyun.js', exportName: 'checkAliyun' },
|
||||
baidu: { modulePath: '@/lib/pancheck/vendor/checkers/baidu.js', exportName: 'checkBaidu' },
|
||||
cmcc: { modulePath: '@/lib/pancheck/vendor/checkers/cmcc.js', exportName: 'checkCMCC' },
|
||||
pan123: { modulePath: '@/lib/pancheck/vendor/checkers/pan123.js', exportName: 'check123' },
|
||||
quark: { modulePath: '@/lib/pancheck/vendor/checkers/quark.js', exportName: 'checkQuark' },
|
||||
tianyi: { modulePath: '@/lib/pancheck/vendor/checkers/tianyi.js', exportName: 'checkTianyi' },
|
||||
uc: { modulePath: '@/lib/pancheck/vendor/checkers/uc.js', exportName: 'checkUC' },
|
||||
xunlei: { modulePath: '@/lib/pancheck/vendor/checkers/xunlei.js', exportName: 'checkXunlei' },
|
||||
const CHECKERS: Record<NetdiskCheckPlatform, (url: string) => Promise<RawCheckerResult>> = {
|
||||
'115': check115,
|
||||
aliyun: checkAliyun,
|
||||
baidu: checkBaidu,
|
||||
cmcc: checkCMCC,
|
||||
pan123: check123,
|
||||
quark: checkQuark,
|
||||
tianyi: checkTianyi,
|
||||
uc: checkUC,
|
||||
xunlei: checkXunlei,
|
||||
};
|
||||
|
||||
const checkerFnCache = new Map<NetdiskCheckPlatform, (url: string) => Promise<RawCheckerResult>>();
|
||||
|
||||
function resolveModulePath(modulePath: string) {
|
||||
return modulePath.replace(/^@\//, `${process.cwd()}/src/`);
|
||||
}
|
||||
|
||||
function getChecker(platform: NetdiskCheckPlatform) {
|
||||
const cached = checkerFnCache.get(platform);
|
||||
if (cached) return cached;
|
||||
const config = CHECKER_EXPORTS[platform];
|
||||
const mod = nodeRequire(resolveModulePath(config.modulePath)) as CheckerModule;
|
||||
const fn = mod[config.exportName];
|
||||
if (typeof fn !== 'function') {
|
||||
throw new Error(`未找到 ${platform} 检测器`);
|
||||
}
|
||||
checkerFnCache.set(platform, fn);
|
||||
return fn;
|
||||
}
|
||||
|
||||
export function normalizeNetdiskCheckUrl(url: string) {
|
||||
return url.trim().replace(/\s+/g, '').replace(/\/+$/, '');
|
||||
}
|
||||
@@ -156,7 +139,10 @@ export async function checkNetdiskLink(platform: NetdiskCheckPlatform, url: stri
|
||||
const runner = (async () => {
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
const checker = getChecker(platform);
|
||||
const checker = CHECKERS[platform];
|
||||
if (typeof checker !== 'function') {
|
||||
throw new Error(`未找到 ${platform} 检测器`);
|
||||
}
|
||||
const raw = await checker(normalizedUrl);
|
||||
const finalResult = toFinalResult(platform, url, normalizedUrl, raw, Date.now() - startedAt);
|
||||
setCachedNetdiskCheckResult(cacheKey, finalResult);
|
||||
|
||||
+10
-15
@@ -1,11 +1,8 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 阿里云盘链接检测
|
||||
* URL格式: https://www.alipan.com/s/{share_id} 或 https://www.aliyundrive.com/s/{share_id}
|
||||
* API: POST https://api.aliyundrive.com/adrive/v3/share_link/get_share_by_anonymous
|
||||
*/
|
||||
async function checkAliyun(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function checkAliyun(link) {
|
||||
const { shareId, error: parseError } = extractParamsAliPan(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
@@ -17,11 +14,11 @@ async function checkAliyun(link) {
|
||||
method: 'POST',
|
||||
body: { share_id: shareId },
|
||||
headers: {
|
||||
'authorization': '',
|
||||
authorization: '',
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://www.alipan.com',
|
||||
'Referer': 'https://www.alipan.com/',
|
||||
'Priority': 'u=1, i',
|
||||
Origin: 'https://www.alipan.com',
|
||||
Referer: 'https://www.alipan.com/',
|
||||
Priority: 'u=1, i',
|
||||
'Sec-Ch-Ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
@@ -39,7 +36,7 @@ async function checkAliyun(link) {
|
||||
return { valid: false, reason: `API返回错误状态码: ${statusCode}` };
|
||||
}
|
||||
|
||||
JSON.parse(body); // 验证可解析即可
|
||||
JSON.parse(body);
|
||||
return { valid: true, reason: '' };
|
||||
} catch (err) {
|
||||
if (err.message === '请求超时') return { valid: false, reason: '请求超时' };
|
||||
@@ -47,7 +44,7 @@ async function checkAliyun(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractParamsAliPan(urlStr) {
|
||||
export function extractParamsAliPan(urlStr) {
|
||||
try {
|
||||
const u = new URL(urlStr);
|
||||
const pathParts = u.pathname.replace(/\/+$/, '').split('/').filter(Boolean);
|
||||
@@ -63,5 +60,3 @@ function extractParamsAliPan(urlStr) {
|
||||
return { shareId: '', error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkAliyun, extractParamsAliPan };
|
||||
+14
-28
@@ -1,11 +1,8 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 百度网盘链接检测
|
||||
* URL格式: https://pan.baidu.com/s/{surl}?pwd={password}
|
||||
* 两步检测: 有密码时先验证密码获取randsk,再调用share/list
|
||||
*/
|
||||
async function checkBaidu(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function checkBaidu(link) {
|
||||
const normalizedLink = normalizeBaiduURL(link);
|
||||
if (!normalizedLink) {
|
||||
return { valid: false, reason: '未找到有效的百度网盘URL' };
|
||||
@@ -26,8 +23,6 @@ async function checkBaidu(link) {
|
||||
|
||||
try {
|
||||
let bdclnd = '';
|
||||
|
||||
// 如果有提取码,先验证
|
||||
if (password) {
|
||||
const verifyURL = `https://pan.baidu.com/share/verify?surl=${encodeURIComponent(shorturl)}&pwd=${encodeURIComponent(password)}`;
|
||||
const formBody = `pwd=${encodeURIComponent(password)}&vcode=&vcode_str=`;
|
||||
@@ -36,7 +31,7 @@ async function checkBaidu(link) {
|
||||
body: formBody,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Referer': normalizedLink,
|
||||
Referer: normalizedLink,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -52,20 +47,16 @@ async function checkBaidu(link) {
|
||||
bdclnd = vData.randsk || '';
|
||||
}
|
||||
|
||||
// 调用 share/list API
|
||||
const apiURL = `https://pan.baidu.com/share/list?web=5&app_id=250528&desc=1&showempty=0&page=1&num=20&order=time&shorturl=${encodeURIComponent(shorturl)}&root=1&view_mode=1&channel=chunlei&web=1&clienttype=0`;
|
||||
const reqHeaders = {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'zh,en-GB;q=0.9,en-US;q=0.8,en;q=0.7,zh-CN;q=0.6',
|
||||
};
|
||||
if (bdclnd) {
|
||||
reqHeaders['Cookie'] = `BDCLND=${bdclnd}`;
|
||||
reqHeaders.Cookie = `BDCLND=${bdclnd}`;
|
||||
}
|
||||
|
||||
const { statusCode, body } = await request(apiURL, {
|
||||
headers: reqHeaders,
|
||||
});
|
||||
|
||||
const { statusCode, body } = await request(apiURL, { headers: reqHeaders });
|
||||
if (statusCode !== 200) {
|
||||
return { valid: false, reason: `API返回错误状态码: ${statusCode}` };
|
||||
}
|
||||
@@ -73,7 +64,6 @@ async function checkBaidu(link) {
|
||||
const result = JSON.parse(body);
|
||||
const errno = result.errno;
|
||||
const errMsg = result.errmsg || result.err_msg || '';
|
||||
|
||||
if (errno === 0) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
@@ -87,12 +77,10 @@ async function checkBaidu(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeBaiduURL(link) {
|
||||
export function normalizeBaiduURL(link) {
|
||||
const cleaned = link.trim();
|
||||
const startIdx = cleaned.indexOf('https://pan.baidu.com/s/');
|
||||
if (startIdx === -1) {
|
||||
return null;
|
||||
}
|
||||
if (startIdx === -1) return null;
|
||||
let endIdx = startIdx;
|
||||
while (endIdx < cleaned.length) {
|
||||
const char = cleaned[endIdx];
|
||||
@@ -103,7 +91,7 @@ function normalizeBaiduURL(link) {
|
||||
return cleaned.substring(startIdx, endIdx).trim();
|
||||
}
|
||||
|
||||
function extractBaiduShareID(shareURL) {
|
||||
export function extractBaiduShareID(shareURL) {
|
||||
try {
|
||||
const u = new URL(shareURL);
|
||||
if (u.pathname.startsWith('/s/')) {
|
||||
@@ -123,11 +111,9 @@ function getFailureReason(errno, errMsg) {
|
||||
if (errMsg) return `分享链接无效 (errno: ${errno}, err_msg: ${errMsg})`;
|
||||
switch (errno) {
|
||||
case -12: return '缺少提取码 (errno: -12)';
|
||||
case -9: return '提取码错误 (errno: -9)';
|
||||
case -9: return '提取码错误 (errno: -9)';
|
||||
case -62: return '请求接口受限 (errno: -62)';
|
||||
case -8: return '分享文件已过期 (errno: -8)';
|
||||
default: return `分享链接无效 (errno: ${errno})`;
|
||||
case -8: return '分享文件已过期 (errno: -8)';
|
||||
default: return `分享链接无效 (errno: ${errno})`;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkBaidu, normalizeBaiduURL, extractBaiduShareID };
|
||||
+13
-41
@@ -1,44 +1,28 @@
|
||||
const crypto = require('crypto');
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 中国移动云盘链接检测
|
||||
* URL格式:
|
||||
* https://yun.139.com/shareweb/#/w/i/{shareID}
|
||||
* https://caiyun.139.com/m/i?{shareID}
|
||||
* API: POST https://share-kd-njs.yun.139.com/yun-share/richlifeApp/devapp/IOutLink/getOutLinkInfoV6
|
||||
* 请求和响应均通过AES-CBC加密(固定密钥)
|
||||
*/
|
||||
import crypto from 'crypto';
|
||||
import { request } from './http';
|
||||
|
||||
const CMCC_AES_KEY = 'PVGDwmcvfs1uV3d1';
|
||||
|
||||
function aesCBCEncrypt(plaintext, key) {
|
||||
export function aesCBCEncrypt(plaintext, key) {
|
||||
const iv = crypto.randomBytes(16);
|
||||
const cipher = crypto.createCipheriv('aes-128-cbc', Buffer.from(key, 'utf-8'), iv);
|
||||
|
||||
// PKCS7 padding
|
||||
const blockSize = 16;
|
||||
const padLen = blockSize - (Buffer.byteLength(plaintext, 'utf-8') % blockSize);
|
||||
const padded = Buffer.concat([
|
||||
Buffer.from(plaintext, 'utf-8'),
|
||||
Buffer.alloc(padLen, padLen),
|
||||
]);
|
||||
|
||||
const padded = Buffer.concat([Buffer.from(plaintext, 'utf-8'), Buffer.alloc(padLen, padLen)]);
|
||||
const encrypted = Buffer.concat([cipher.update(padded), cipher.final()]);
|
||||
return Buffer.concat([iv, encrypted]).toString('base64');
|
||||
}
|
||||
|
||||
function aesCBCDecrypt(encryptedBase64, key) {
|
||||
export function aesCBCDecrypt(encryptedBase64, key) {
|
||||
const rawData = Buffer.from(encryptedBase64, 'base64');
|
||||
if (rawData.length < 16) throw new Error('加密数据长度不足');
|
||||
|
||||
const iv = rawData.subarray(0, 16);
|
||||
const ciphertext = rawData.subarray(16);
|
||||
|
||||
const decipher = crypto.createDecipheriv('aes-128-cbc', Buffer.from(key, 'utf-8'), iv);
|
||||
const decrypted = Buffer.concat([decipher.update(ciphertext), decipher.final()]);
|
||||
|
||||
// 去除PKCS7填充
|
||||
const padLen = decrypted[decrypted.length - 1];
|
||||
if (padLen > 0 && padLen <= 16) {
|
||||
return decrypted.subarray(0, decrypted.length - padLen).toString('utf-8');
|
||||
@@ -46,7 +30,7 @@ function aesCBCDecrypt(encryptedBase64, key) {
|
||||
return decrypted.toString('utf-8');
|
||||
}
|
||||
|
||||
async function checkCMCC(link) {
|
||||
export async function checkCMCC(link) {
|
||||
const shareID = extractShareID(link);
|
||||
if (!shareID) {
|
||||
return { valid: false, reason: '链接格式无效:无法提取分享ID' };
|
||||
@@ -71,10 +55,7 @@ async function checkCMCC(link) {
|
||||
},
|
||||
};
|
||||
|
||||
const jsonStr = JSON.stringify(requestData);
|
||||
const encryptedData = aesCBCEncrypt(jsonStr, CMCC_AES_KEY);
|
||||
const encryptedJSON = JSON.stringify(encryptedData);
|
||||
|
||||
const encryptedJSON = JSON.stringify(aesCBCEncrypt(JSON.stringify(requestData), CMCC_AES_KEY));
|
||||
const { statusCode, body } = await request(
|
||||
'https://share-kd-njs.yun.139.com/yun-share/richlifeApp/devapp/IOutLink/getOutLinkInfoV6',
|
||||
{
|
||||
@@ -82,7 +63,7 @@ async function checkCMCC(link) {
|
||||
body: encryptedJSON,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
'hcy-cool-flag': '1',
|
||||
'x-deviceinfo': '||3|12.27.0|chrome|131.0.0.0|5c7c68368f048245e1ce47f1c0f8f2d0||windows 10|1536X695|zh-CN|||',
|
||||
@@ -94,19 +75,12 @@ async function checkCMCC(link) {
|
||||
return { valid: false, reason: `API返回错误状态码: ${statusCode}` };
|
||||
}
|
||||
|
||||
// 解密响应
|
||||
const decryptedData = aesCBCDecrypt(body.trim(), CMCC_AES_KEY);
|
||||
const response = JSON.parse(decryptedData);
|
||||
|
||||
const resultCode = response.resultCode;
|
||||
const desc = response.desc;
|
||||
const data = response.data;
|
||||
|
||||
if (resultCode === '0' && data != null) {
|
||||
const response = JSON.parse(aesCBCDecrypt(body.trim(), CMCC_AES_KEY));
|
||||
if (response.resultCode === '0' && response.data != null) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
const failReason = desc || (resultCode ? `错误码: ${resultCode}` : '获取分享信息失败');
|
||||
const failReason = response.desc || (response.resultCode ? `错误码: ${response.resultCode}` : '获取分享信息失败');
|
||||
return { valid: false, reason: failReason };
|
||||
} catch (err) {
|
||||
if (err.message === '请求超时') return { valid: false, reason: '请求超时' };
|
||||
@@ -114,9 +88,7 @@ async function checkCMCC(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractShareID(shareURL) {
|
||||
export function extractShareID(shareURL) {
|
||||
const match = shareURL.match(/https:\/\/(?:yun\.139\.com\/shareweb\/#\/w\/i\/|caiyun\.139\.com\/m\/i\?)([^&]+)/);
|
||||
return match ? match[1] : '';
|
||||
}
|
||||
|
||||
module.exports = { checkCMCC, extractShareID, aesCBCEncrypt, aesCBCDecrypt };
|
||||
+10
-11
@@ -1,24 +1,25 @@
|
||||
const https = require('https');
|
||||
const http = require('http');
|
||||
const { URL } = require('url');
|
||||
// @ts-nocheck
|
||||
|
||||
const DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36';
|
||||
const DEFAULT_HEADERS = {
|
||||
'accept': 'application/json;charset=UTF-8',
|
||||
import http from 'http';
|
||||
import https from 'https';
|
||||
|
||||
export const DEFAULT_UA = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36';
|
||||
export const DEFAULT_HEADERS = {
|
||||
accept: 'application/json;charset=UTF-8',
|
||||
'accept-language': 'en,zh-CN;q=0.9,zh;q=0.8',
|
||||
'user-agent': DEFAULT_UA,
|
||||
'cache-control': 'no-cache',
|
||||
'pragma': 'no-cache',
|
||||
pragma: 'no-cache',
|
||||
};
|
||||
|
||||
function request(url, options = {}) {
|
||||
export function request(url, options = {}) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = options.timeout || 15000;
|
||||
const parsedUrl = new URL(url);
|
||||
const transport = parsedUrl.protocol === 'https:' ? https : http;
|
||||
|
||||
const headers = { ...DEFAULT_HEADERS, ...(options.headers || {}) };
|
||||
delete headers['Content-Type']; // handled below
|
||||
delete headers['Content-Type'];
|
||||
|
||||
const reqOptions = {
|
||||
hostname: parsedUrl.hostname,
|
||||
@@ -56,5 +57,3 @@ function request(url, options = {}) {
|
||||
req.end();
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = { request, DEFAULT_UA, DEFAULT_HEADERS };
|
||||
+8
-16
@@ -1,11 +1,8 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 115网盘链接检测
|
||||
* URL格式: https://115cdn.com/s/{share_code}?password={receive_code}
|
||||
* API: GET https://115cdn.com/webapi/share/snap
|
||||
*/
|
||||
async function check115(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function check115(link) {
|
||||
const { shareCode, receiveCode, error: parseError } = extractParams115(link);
|
||||
if (parseError || !shareCode || !receiveCode) {
|
||||
return { valid: false, reason: parseError || (!shareCode ? '缺少分享码' : '缺少提取码') };
|
||||
@@ -15,7 +12,7 @@ async function check115(link) {
|
||||
const apiURL = `https://115cdn.com/webapi/share/snap?share_code=${encodeURIComponent(shareCode)}&offset=0&limit=20&receive_code=${encodeURIComponent(receiveCode)}&cid=`;
|
||||
const { statusCode, body } = await request(apiURL, {
|
||||
headers: {
|
||||
'Referer': `https://115cdn.com/s/${shareCode}?password=${receiveCode}&`,
|
||||
Referer: `https://115cdn.com/s/${shareCode}?password=${receiveCode}&`,
|
||||
'Sec-Ch-Ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
@@ -23,7 +20,7 @@ async function check115(link) {
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'same-origin',
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Priority': 'u=1, i',
|
||||
Priority: 'u=1, i',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -32,10 +29,8 @@ async function check115(link) {
|
||||
}
|
||||
|
||||
const data = JSON.parse(body);
|
||||
|
||||
if (data.state === true && data.errno === 0) {
|
||||
let shareState = data.data?.share_state || 0;
|
||||
// 兼容部分响应只在 shareinfo 中返回 share_state
|
||||
if (shareState === 0 && data.data?.shareinfo?.share_state) {
|
||||
shareState = data.data.shareinfo.share_state;
|
||||
}
|
||||
@@ -44,8 +39,7 @@ async function check115(link) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
const failReason = (data.data?.shareinfo?.forbid_reason || '').trim()
|
||||
|| `链接状态异常(share_state=${shareState})`;
|
||||
const failReason = (data.data?.shareinfo?.forbid_reason || '').trim() || `链接状态异常(share_state=${shareState})`;
|
||||
return { valid: false, reason: failReason };
|
||||
}
|
||||
|
||||
@@ -56,7 +50,7 @@ async function check115(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractParams115(urlStr) {
|
||||
export function extractParams115(urlStr) {
|
||||
try {
|
||||
const u = new URL(urlStr);
|
||||
const pathParts = u.pathname.replace(/\/+$/, '').split('/');
|
||||
@@ -73,5 +67,3 @@ function extractParams115(urlStr) {
|
||||
return { shareCode: '', receiveCode: '', error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { check115, extractParams115 };
|
||||
+9
-25
@@ -1,13 +1,8 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 123网盘链接检测
|
||||
* URL格式: https://www.123pan.com/s/{shareKey}
|
||||
* API: GET https://www.123pan.com/api/share/info?shareKey={shareKey}
|
||||
*
|
||||
* 注意: 此检测器采用保守策略,超时/403/请求错误均视为有效,避免误判
|
||||
*/
|
||||
async function check123(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function check123(link) {
|
||||
const { shareKey, error: parseError } = extractShareKey123(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
@@ -21,35 +16,27 @@ async function check123(link) {
|
||||
},
|
||||
});
|
||||
|
||||
// 403视为有效(访问限制,不是链接失效)
|
||||
if (statusCode === 403) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
if (statusCode !== 200) {
|
||||
return { valid: true, reason: '' }; // 非预期状态码也视为有效,避免误判
|
||||
}
|
||||
if (statusCode === 403) return { valid: true, reason: '' };
|
||||
if (statusCode !== 200) return { valid: true, reason: '' };
|
||||
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(body);
|
||||
} catch (_) {
|
||||
return { valid: true, reason: '' }; // JSON解析错误视为有效
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
// code==0 或 HasPwd==true 均视为有效
|
||||
if (data.code === 0 || data.data?.HasPwd === true) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
return { valid: false, reason: '链接已失效' };
|
||||
} catch (err) {
|
||||
// 超时和请求错误均视为有效,避免误判
|
||||
} catch (_) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
}
|
||||
|
||||
function extractShareKey123(urlStr) {
|
||||
export function extractShareKey123(urlStr) {
|
||||
const patterns = [
|
||||
/https?:\/\/(?:www\.)?(?:123684|123685|123912|123pan|123592|123865)\.com\/s\/([a-zA-Z0-9-]+)/,
|
||||
/https?:\/\/(?:www\.)?123pan\.cn\/s\/([a-zA-Z0-9-]+)/,
|
||||
@@ -62,7 +49,6 @@ function extractShareKey123(urlStr) {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: 从URL路径中提取
|
||||
try {
|
||||
const u = new URL(urlStr);
|
||||
const pathParts = u.pathname.replace(/\/+$/, '').split('/').filter(Boolean);
|
||||
@@ -73,5 +59,3 @@ function extractShareKey123(urlStr) {
|
||||
|
||||
return { shareKey: '', error: '无法从URL中提取shareKey' };
|
||||
}
|
||||
|
||||
module.exports = { check123, extractShareKey123 };
|
||||
+11
-18
@@ -1,18 +1,14 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 夸克网盘链接检测
|
||||
* URL格式: https://pan.quark.cn/s/{pwd_id}?pwd={passcode}
|
||||
* 两步检测: 先获取stoken,再验证文件列表
|
||||
*/
|
||||
async function checkQuark(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function checkQuark(link) {
|
||||
const { resId, pwd, error: parseError } = extractParamsQuark(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
}
|
||||
|
||||
try {
|
||||
// Step 1: 获取 stoken
|
||||
const tokenURL = 'https://drive-h.quark.cn/1/clouddrive/share/sharepage/token';
|
||||
const { statusCode: status1, body: body1 } = await request(tokenURL, {
|
||||
method: 'POST',
|
||||
@@ -23,8 +19,8 @@ async function checkQuark(link) {
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://pan.quark.cn',
|
||||
'Referer': 'https://pan.quark.cn/',
|
||||
Origin: 'https://pan.quark.cn',
|
||||
Referer: 'https://pan.quark.cn/',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -40,16 +36,15 @@ async function checkQuark(link) {
|
||||
return { valid: false, reason: '分享链接无效:未获取到访问令牌' };
|
||||
}
|
||||
|
||||
// Step 2: 获取文件列表
|
||||
const detailURL = `https://drive-pc.quark.cn/1/clouddrive/share/sharepage/detail?pwd_id=${encodeURIComponent(resId)}&stoken=${encodeURIComponent(tokenResp.data.stoken)}&ver=2&pr=ucpro`;
|
||||
const { statusCode: status2, body: body2 } = await request(detailURL, {
|
||||
headers: {
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'zh-CN,zh;q=0.9',
|
||||
'Cache-Control': 'no-cache',
|
||||
'Origin': 'https://pan.quark.cn',
|
||||
'Referer': 'https://pan.quark.cn/',
|
||||
'Pragma': 'no-cache',
|
||||
Origin: 'https://pan.quark.cn',
|
||||
Referer: 'https://pan.quark.cn/',
|
||||
Pragma: 'no-cache',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -69,7 +64,7 @@ async function checkQuark(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractParamsQuark(rawURL) {
|
||||
export function extractParamsQuark(rawURL) {
|
||||
const urlRegex = /^https:\/\/(?:pan\.quark\.cn|pan\.qoark\.cn)\/s\/[a-zA-Z0-9]+(?:\?[^#]*)?(?:#.*)?$/;
|
||||
if (!urlRegex.test(rawURL)) {
|
||||
return { resId: '', pwd: '', error: '无效的URL格式' };
|
||||
@@ -93,5 +88,3 @@ function extractParamsQuark(rawURL) {
|
||||
return { resId: '', pwd: '', error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkQuark, extractParamsQuark };
|
||||
+8
-23
@@ -1,14 +1,8 @@
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 天翼云盘链接检测
|
||||
* URL格式:
|
||||
* https://cloud.189.cn/web/share?code=xxx
|
||||
* https://cloud.189.cn/t/xxx
|
||||
* https://h5.cloud.189.cn/share.html#/t/xxx
|
||||
* API: GET https://cloud.189.cn/api/open/share/getShareInfoByCodeV2.action
|
||||
*/
|
||||
async function checkTianyi(link) {
|
||||
import { request } from './http';
|
||||
|
||||
export async function checkTianyi(link) {
|
||||
const { codeValue, accessCode, refererValue, error: parseError } = extractCodeFromURL(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
@@ -16,7 +10,6 @@ async function checkTianyi(link) {
|
||||
|
||||
try {
|
||||
const noCache = Math.random();
|
||||
// 如果有访问码,需要将访问码包含在shareCode参数中
|
||||
let shareCodeParam = codeValue;
|
||||
if (accessCode) {
|
||||
shareCodeParam = `${codeValue}(访问码:${accessCode})`;
|
||||
@@ -25,8 +18,8 @@ async function checkTianyi(link) {
|
||||
const apiURL = `https://cloud.189.cn/api/open/share/getShareInfoByCodeV2.action?noCache=${noCache}&shareCode=${encodeURIComponent(shareCodeParam)}`;
|
||||
const { statusCode, body } = await request(apiURL, {
|
||||
headers: {
|
||||
'Priority': 'u=1, i',
|
||||
'Referer': refererValue,
|
||||
Priority: 'u=1, i',
|
||||
Referer: refererValue,
|
||||
'Sec-Ch-Ua': '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
@@ -54,21 +47,17 @@ async function checkTianyi(link) {
|
||||
}
|
||||
}
|
||||
|
||||
function extractCodeFromURL(urlStr) {
|
||||
export function extractCodeFromURL(urlStr) {
|
||||
try {
|
||||
const u = new URL(urlStr);
|
||||
let codeValue = '';
|
||||
let accessCode = '';
|
||||
|
||||
// 1. 从查询参数获取code
|
||||
codeValue = u.searchParams.get('code') || '';
|
||||
|
||||
// 2. 从路径获取 /t/xxx
|
||||
if (!codeValue && u.pathname.startsWith('/t/')) {
|
||||
codeValue = u.pathname.replace('/t/', '').split('/')[0];
|
||||
}
|
||||
|
||||
// 3. 从hash获取 #/t/xxx
|
||||
if (!codeValue && u.hash) {
|
||||
const fragment = u.hash.replace(/^#/, '');
|
||||
if (fragment.startsWith('/t/')) {
|
||||
@@ -82,9 +71,7 @@ function extractCodeFromURL(urlStr) {
|
||||
return { codeValue: '', accessCode: '', refererValue: '', error: '输入URL中未找到code参数' };
|
||||
}
|
||||
|
||||
// 提取访问码(访问码:xxx)
|
||||
const accessCodePattern = /[((]访问码[::]\s*([a-zA-Z0-9]+)[))]/;
|
||||
const match = urlStr.match(accessCodePattern);
|
||||
const match = urlStr.match(/[((]访问码[::]\s*([a-zA-Z0-9]+)[))]/);
|
||||
if (match && match[1]) {
|
||||
accessCode = match[1];
|
||||
}
|
||||
@@ -94,5 +81,3 @@ function extractCodeFromURL(urlStr) {
|
||||
return { codeValue: '', accessCode: '', refererValue: '', error: e.message };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { checkTianyi, extractCodeFromURL };
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
const { request, DEFAULT_UA } = require('./http');
|
||||
|
||||
/**
|
||||
* UC网盘链接检测
|
||||
* URL格式: https://drive.uc.cn/s/{shareID}
|
||||
* 检测方法: 页面爬取,通过关键词判断有效性
|
||||
*/
|
||||
async function checkUC(link) {
|
||||
const { shareID, error: parseError } = extractShareIDFromURL(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `https://drive.uc.cn/s/${shareID}`;
|
||||
const { statusCode, body } = await request(url, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Mobile Safari/537.36',
|
||||
},
|
||||
});
|
||||
|
||||
if (statusCode !== 200) {
|
||||
return { valid: false, reason: `HTTP状态码: ${statusCode}` };
|
||||
}
|
||||
|
||||
const pageText = body.toLowerCase();
|
||||
const errorKeywords = ['失效', '不存在', '违规', '删除', '已过期', '被取消'];
|
||||
|
||||
for (const keyword of errorKeywords) {
|
||||
if (pageText.includes(keyword)) {
|
||||
return { valid: false, reason: '链接已失效' };
|
||||
}
|
||||
}
|
||||
|
||||
const validKeywords = ['文件', '分享'];
|
||||
for (const keyword of validKeywords) {
|
||||
if (pageText.includes(keyword)) {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
}
|
||||
|
||||
return { valid: false, reason: '无法判断链接有效性' };
|
||||
} catch (err) {
|
||||
if (err.message === '请求超时') return { valid: true, reason: '' }; // 超时视为有效,避免误判
|
||||
return { valid: true, reason: '' }; // 连接错误也视为有效,避免误判
|
||||
}
|
||||
}
|
||||
|
||||
function extractShareIDFromURL(urlStr) {
|
||||
const pattern = /https?:\/\/drive\.uc\.cn\/s\/([a-zA-Z0-9]+)/;
|
||||
const match = urlStr.match(pattern);
|
||||
if (match && match[1]) {
|
||||
return { shareID: match[1], error: null };
|
||||
}
|
||||
return { shareID: '', error: '无法从URL中提取share_id' };
|
||||
}
|
||||
|
||||
module.exports = { checkUC, extractShareIDFromURL };
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
// @ts-nocheck
|
||||
|
||||
import { request } from './http';
|
||||
|
||||
export async function checkUC(link) {
|
||||
const { shareID, error: parseError } = extractShareIDFromURL(link);
|
||||
if (parseError) {
|
||||
return { valid: false, reason: '链接格式无效: ' + parseError };
|
||||
}
|
||||
|
||||
try {
|
||||
const url = `https://drive.uc.cn/s/${shareID}`;
|
||||
const { statusCode, body } = await request(url, {
|
||||
headers: {
|
||||
'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G975F) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.101 Mobile Safari/537.36',
|
||||
},
|
||||
});
|
||||
|
||||
if (statusCode !== 200) {
|
||||
return { valid: false, reason: `HTTP状态码: ${statusCode}` };
|
||||
}
|
||||
|
||||
const pageText = body.toLowerCase();
|
||||
for (const keyword of ['失效', '不存在', '违规', '删除', '已过期', '被取消']) {
|
||||
if (pageText.includes(keyword)) return { valid: false, reason: '链接已失效' };
|
||||
}
|
||||
for (const keyword of ['文件', '分享']) {
|
||||
if (pageText.includes(keyword)) return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
return { valid: false, reason: '无法判断链接有效性' };
|
||||
} catch (err) {
|
||||
if (err.message === '请求超时') return { valid: true, reason: '' };
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
}
|
||||
|
||||
export function extractShareIDFromURL(urlStr) {
|
||||
const match = urlStr.match(/https?:\/\/drive\.uc\.cn\/s\/([a-zA-Z0-9]+)/);
|
||||
if (match && match[1]) {
|
||||
return { shareID: match[1], error: null };
|
||||
}
|
||||
return { shareID: '', error: '无法从URL中提取share_id' };
|
||||
}
|
||||
+28
-59
@@ -1,12 +1,8 @@
|
||||
const crypto = require('crypto');
|
||||
const zlib = require('zlib');
|
||||
const { request } = require('./http');
|
||||
// @ts-nocheck
|
||||
|
||||
/**
|
||||
* 迅雷云盘链接检测
|
||||
* URL格式: https://pan.xunlei.com/s/{share_id}?pwd={pass_code}
|
||||
* 两步检测: 先获取captcha token,再调用share API
|
||||
*/
|
||||
import crypto from 'crypto';
|
||||
import zlib from 'zlib';
|
||||
import { request } from './http';
|
||||
|
||||
const XUNLEI_DEVICE_ID = '5505bd0cab8c9469b98e5891d9fb3e0d';
|
||||
const XUNLEI_CLIENT_ID = 'ZUBzD9J_XPXfn7f7';
|
||||
@@ -26,18 +22,16 @@ const CAPTCHA_ALGORITHMS = [
|
||||
'ThTWPG5eC0UBqlbQ+04nZAptqGCdpv9o55A',
|
||||
];
|
||||
|
||||
function getCaptchaSign(clientID, clientVersion, packageName, deviceID) {
|
||||
export function getCaptchaSign(clientID, clientVersion, packageName, deviceID) {
|
||||
const timestamp = Date.now().toString();
|
||||
let str = `${clientID}${clientVersion}${packageName}${deviceID}${timestamp}`;
|
||||
|
||||
for (const algorithm of CAPTCHA_ALGORITHMS) {
|
||||
str = crypto.createHash('md5').update(str + algorithm).digest('hex');
|
||||
}
|
||||
|
||||
return { timestamp, sign: `1.${str}` };
|
||||
}
|
||||
|
||||
async function getCaptchaToken(action, metas = {}) {
|
||||
export async function getCaptchaToken(action, metas = {}) {
|
||||
const { timestamp, sign: captchaSign } = getCaptchaSign(
|
||||
XUNLEI_CLIENT_ID, XUNLEI_CLIENT_VERSION, XUNLEI_PACKAGE_NAME, XUNLEI_DEVICE_ID
|
||||
);
|
||||
@@ -47,22 +41,20 @@ async function getCaptchaToken(action, metas = {}) {
|
||||
metas.client_version = XUNLEI_CLIENT_VERSION;
|
||||
metas.package_name = XUNLEI_PACKAGE_NAME;
|
||||
|
||||
const requestBody = {
|
||||
action,
|
||||
captcha_token: '',
|
||||
client_id: XUNLEI_CLIENT_ID,
|
||||
device_id: XUNLEI_DEVICE_ID,
|
||||
meta: metas,
|
||||
redirect_uri: 'xlaccsdk01://xunlei.com/callback?state=harbor',
|
||||
};
|
||||
|
||||
const { statusCode, body, headers } = await request(
|
||||
'https://xluser-ssl.xunlei.com/v1/shield/captcha/init',
|
||||
{
|
||||
method: 'POST',
|
||||
body: requestBody,
|
||||
body: {
|
||||
action,
|
||||
captcha_token: '',
|
||||
client_id: XUNLEI_CLIENT_ID,
|
||||
device_id: XUNLEI_DEVICE_ID,
|
||||
meta: metas,
|
||||
redirect_uri: 'xlaccsdk01://xunlei.com/callback?state=harbor',
|
||||
},
|
||||
headers: {
|
||||
'Accept': 'application/json;charset=UTF-8',
|
||||
Accept: 'application/json;charset=UTF-8',
|
||||
'Content-Type': 'application/json',
|
||||
'User-Agent': XUNLEI_UA,
|
||||
'X-Device-Id': XUNLEI_DEVICE_ID,
|
||||
@@ -77,7 +69,6 @@ async function getCaptchaToken(action, metas = {}) {
|
||||
}
|
||||
|
||||
let respBody = body;
|
||||
// 解压 gzip/deflate
|
||||
const encoding = (headers['content-encoding'] || '').toLowerCase();
|
||||
if (encoding === 'gzip') {
|
||||
respBody = zlib.gunzipSync(Buffer.from(body, 'binary')).toString('utf-8');
|
||||
@@ -86,16 +77,12 @@ async function getCaptchaToken(action, metas = {}) {
|
||||
}
|
||||
|
||||
const data = JSON.parse(respBody);
|
||||
if (data.url) {
|
||||
throw new Error(`需要验证: ${data.url}`);
|
||||
}
|
||||
if (!data.captcha_token) {
|
||||
throw new Error('未获取到验证码token');
|
||||
}
|
||||
if (data.url) throw new Error(`需要验证: ${data.url}`);
|
||||
if (!data.captcha_token) throw new Error('未获取到验证码token');
|
||||
return data.captcha_token;
|
||||
}
|
||||
|
||||
async function checkXunlei(link) {
|
||||
export async function checkXunlei(link) {
|
||||
const shareID = extractShareID(link);
|
||||
if (!shareID) {
|
||||
return { valid: false, reason: '链接格式无效:无法提取share_id' };
|
||||
@@ -108,7 +95,6 @@ async function checkXunlei(link) {
|
||||
} catch (_) {}
|
||||
|
||||
try {
|
||||
// Step 1: 获取 captcha token
|
||||
let captchaToken = '';
|
||||
try {
|
||||
captchaToken = await getCaptchaToken('get:/drive/v1/share', {
|
||||
@@ -119,17 +105,14 @@ async function checkXunlei(link) {
|
||||
client_version: '1.92.10',
|
||||
user_id: '0',
|
||||
});
|
||||
} catch (_) {
|
||||
// token获取失败时继续,不带token请求
|
||||
}
|
||||
} catch (_) {}
|
||||
|
||||
// Step 2: 调用share API
|
||||
const apiURL = `https://api-pan.xunlei.com/drive/v1/share?share_id=${encodeURIComponent(shareID)}&pass_code=${encodeURIComponent(passCode)}&limit=100&pass_code_token=&page_token=&thumbnail_size=SIZE_SMALL`;
|
||||
const reqHeaders = {
|
||||
'Accept': '*/*',
|
||||
Accept: '*/*',
|
||||
'Content-Type': 'application/json',
|
||||
'Origin': 'https://pan.xunlei.com',
|
||||
'Referer': 'https://pan.xunlei.com/',
|
||||
Origin: 'https://pan.xunlei.com',
|
||||
Referer: 'https://pan.xunlei.com/',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36',
|
||||
'Accept-Encoding': 'gzip, deflate',
|
||||
'X-Client-Id': XUNLEI_CLIENT_ID,
|
||||
@@ -140,9 +123,7 @@ async function checkXunlei(link) {
|
||||
}
|
||||
|
||||
const { statusCode, body, headers } = await request(apiURL, { headers: reqHeaders });
|
||||
|
||||
let respBody = body;
|
||||
// 解压
|
||||
const encoding = (headers['content-encoding'] || '').toLowerCase();
|
||||
if (encoding === 'gzip') {
|
||||
respBody = zlib.gunzipSync(Buffer.from(body, 'binary')).toString('utf-8');
|
||||
@@ -151,14 +132,12 @@ async function checkXunlei(link) {
|
||||
}
|
||||
|
||||
if (statusCode !== 200) {
|
||||
let isRateLimited = false;
|
||||
try {
|
||||
const errData = JSON.parse(respBody);
|
||||
if (errData.error_code === 9) isRateLimited = true;
|
||||
return {
|
||||
valid: false,
|
||||
reason: `HTTP状态码: ${statusCode}, 响应: ${respBody}`,
|
||||
isRateLimited,
|
||||
isRateLimited: errData.error_code === 9,
|
||||
};
|
||||
} catch (_) {
|
||||
return { valid: false, reason: `HTTP状态码: ${statusCode}` };
|
||||
@@ -166,26 +145,16 @@ async function checkXunlei(link) {
|
||||
}
|
||||
|
||||
const apiResp = JSON.parse(respBody);
|
||||
|
||||
if (apiResp.share_status === 'OK') {
|
||||
return { valid: true, reason: '' };
|
||||
}
|
||||
|
||||
if (apiResp.error) {
|
||||
return { valid: false, reason: apiResp.error };
|
||||
}
|
||||
|
||||
const statusText = apiResp.share_status_text || `分享状态: ${apiResp.share_status}`;
|
||||
return { valid: false, reason: statusText };
|
||||
if (apiResp.share_status === 'OK') return { valid: true, reason: '' };
|
||||
if (apiResp.error) return { valid: false, reason: apiResp.error };
|
||||
return { valid: false, reason: apiResp.share_status_text || `分享状态: ${apiResp.share_status}` };
|
||||
} catch (err) {
|
||||
if (err.message === '请求超时') return { valid: true, reason: '' }; // 超时视为有效,避免误判
|
||||
if (err.message === '请求超时') return { valid: true, reason: '' };
|
||||
return { valid: false, reason: `检测失败: ${err.message}` };
|
||||
}
|
||||
}
|
||||
|
||||
function extractShareID(shareURL) {
|
||||
export function extractShareID(shareURL) {
|
||||
const match = shareURL.match(/pan\.xunlei\.com\/s\/([^?/#]+)/);
|
||||
return match ? match[1] : '';
|
||||
}
|
||||
|
||||
module.exports = { checkXunlei, extractShareID, getCaptchaSign, getCaptchaToken };
|
||||
Reference in New Issue
Block a user