Skip to content

Commit 8542367

Browse files
author
Taois
committed
feat: 发布新版本,支持hipy的T4源
1 parent 59e28d8 commit 8542367

File tree

22 files changed

+2049
-38
lines changed

22 files changed

+2049
-38
lines changed

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ nodejs作为服务端的drpy实现。全面升级异步写法
99
* [本地配置接口-动态本地](/config?pwd=$pwd)
1010
* [本地配置接口-动态外网/局域网](/config/1?pwd=$pwd)
1111
* [其他配置接口-订阅过滤](/docs/sub.md)
12+
* [python环境](/docs/pyenv.md)
1213
* [猫源调试教程](/docs/catDebug.md)
1314
* [代码加解密工具](/admin/encoder)
1415
* [V我50支付凭证生成器](/authcoder?len=10&number=1)
@@ -24,6 +25,10 @@ nodejs作为服务端的drpy实现。全面升级异步写法
2425

2526
## 更新记录
2627

28+
### 20250817
29+
30+
更新至V1.2.13
31+
2732
### 20250815
2833

2934
更新至V1.2.12

controllers/api.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ export default (fastify, options, done) => {
9090
invokeMethod = 'homeVod';
9191
break;
9292
case '一级':
93-
invokeMethod = 'cate';
93+
invokeMethod = 'category';
9494
break;
9595
case '二级':
9696
invokeMethod = 'detail';
@@ -141,7 +141,7 @@ export default (fastify, options, done) => {
141141
}
142142
}
143143
// 分类逻辑
144-
const result = await apiEngine.cate(modulePath, env, query.t, pg, 1, extend);
144+
const result = await apiEngine.category(modulePath, env, query.t, pg, 1, extend);
145145
return reply.send(result);
146146
}
147147

controllers/config.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -312,21 +312,24 @@ async function generateSiteJSON(options, requestHost, sub, pwd) {
312312
}
313313

314314
// 根据用户是否启用py源去生成对应配置
315-
if (ENV.get('enable_py', '1') === '1') {
315+
const enable_py = ENV.get('enable_py', '1');
316+
if (enable_py === '1' || enable_py === '2') {
316317
const py_files = readdirSync(pyDir);
317-
let py_valid_files = py_files.filter((file) => file.endsWith('.py') && !file.startsWith('_')); // 筛选出不是 "_" 开头的 .py 文件
318+
const api_type = enable_py === '1' ? 3 : 4;
319+
let py_valid_files = py_files.filter((file) => file.endsWith('.py') && !file.startsWith('_') && !file.startsWith('base_')); // 筛选出不是 "_" 开头的 .py 文件
318320
// log(py_valid_files);
319-
log(`开始生成python的t3配置,pyDir:${pyDir},源数量: ${py_valid_files.length}`);
321+
log(`开始生成python的T${api_type}配置,pyDir:${pyDir},源数量: ${py_valid_files.length}`);
320322

321323
const py_tasks = py_valid_files.map((file) => {
322324
return {
323325
func: async ({file, pyDir, requestHost, pwd, SitesMap}) => {
324326
const baseName = path.basename(file, '.py'); // 去掉文件扩展名
325327
const extJson = path.join(pyDir, baseName + '.json');
326-
let api = `${requestHost}/py/${file}`;
328+
let api = enable_py === '1' ? `${requestHost}/py/${file}` : `${requestHost}/api/${baseName}?do=py`; // 使用请求的 host 地址,避免硬编码端口
327329
let ext = existsSync(extJson) ? `${requestHost}/py/${file}` : '';
328330
if (pwd) {
329-
api += `?pwd=${pwd}`;
331+
api += api_type === 3 ? '?' : '&';
332+
api += `pwd=${pwd}`;
330333
if (ext) {
331334
ext += `?pwd=${pwd}`;
332335
}
@@ -381,7 +384,7 @@ async function generateSiteJSON(options, requestHost, sub, pwd) {
381384
const site = {
382385
key: fileSite.key,
383386
name: fileSite.name,
384-
type: 3, // 固定值
387+
type: api_type, // 固定值
385388
api,
386389
...ruleMeta,
387390
ext: fileSite.ext || "", // 固定为空字符串

docs/pyenv.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# 安装python环境依赖
2+
3+
```shell
4+
pip install -r spider/py/base/requirements.txt
5+
```

docs/updateRecord.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
# drpyS更新记录
22

3+
### 20250817
4+
5+
更新至V1.2.13
6+
7+
支持hipy源T4,参考[python环境](/docs/pyenv.md) 进行python依赖安装。
8+
需要保证终端输入 `python` 能正常识别到即可(保证本地安装了python并且有环境变量)
9+
10+
1. 增加 `hipy` 适配器
11+
2. 新增依赖 `python-shell` 需要手动安装
12+
3. 统一libs接口 `cate` 改为 `category`
13+
314
### 20250815
415

516
更新至V1.2.12

libs/bug_lib/dr2Adapter.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1098,7 +1098,7 @@ async function categoryParse(cateObj, rule) {
10981098
// 同时设置全局变量,确保脚本可以访问
10991099
globalThis.MY_FL = cateObj.extend;
11001100

1101-
const TYPE = "cate";
1101+
const TYPE = "category";
11021102
var input = MY_URL;
11031103
const MY_PAGE = cateObj.pg;
11041104
var desc = "";
@@ -1760,10 +1760,6 @@ export async function category(filePath, env, tid, pg, filter, extend) {
17601760
return parseJsonResult(result);
17611761
}
17621762

1763-
export async function cate(filePath, env, tid, pg, filter, extend) {
1764-
return category(filePath, env, tid, pg, filter, extend);
1765-
}
1766-
17671763
export async function detail(filePath, env, vod_url) {
17681764
const { rule } = await init(filePath, env);
17691765
vod_url=Array.isArray(vod_url) ? vod_url[0] : vod_url;
@@ -2072,7 +2068,6 @@ const drpy2 = {
20722068
home,
20732069
homeVod,
20742070
category,
2075-
cate,
20762071
detail,
20772072
search,
20782073
play,

libs/catvod.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ const homeVod = async function (filePath, env) {
164164
}
165165

166166

167-
const cate = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
167+
const category = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
168168
const moduleObject = await init(filePath, env);
169169
return json2Object(await moduleObject.category(tid, pg, filter, extend));
170170
}
@@ -201,7 +201,7 @@ export default {
201201
init,
202202
home,
203203
homeVod,
204-
cate,
204+
category,
205205
detail,
206206
search,
207207
play,

libs/drpy2.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ const homeVod = async function (filePath, env) {
5454
}
5555

5656

57-
const cate = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
57+
const category = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
5858
const moduleObject = await init(filePath, env);
5959
return json2Object(await moduleObject.category(tid, pg, filter, extend));
6060
}
@@ -91,7 +91,7 @@ export default {
9191
init,
9292
home,
9393
homeVod,
94-
cate,
94+
category,
9595
detail,
9696
search,
9797
play,

libs/drpyS.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1329,7 +1329,7 @@ export async function homeVod(filePath, env) {
13291329
});
13301330
}
13311331

1332-
export async function cate(filePath, env, tid, pg = 1, filter = 1, extend = {}) {
1332+
export async function category(filePath, env, tid, pg = 1, filter = 1, extend = {}) {
13331333
return await invokeMethod(filePath, env, '一级', [tid, pg, filter, extend], {
13341334
input: '$.url',
13351335
MY_URL: '$.url'

libs/hipy.js

Lines changed: 146 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,23 @@
1-
import {computeHash, deepCopy} from "../utils/utils.js";
1+
import path from "path";
22
import {readFile} from "fs/promises";
3+
import {getSitesMap} from "../utils/sites-map.js";
4+
import {computeHash, deepCopy, getNowTime} from "../utils/utils.js";
5+
import {fileURLToPath} from 'url';
36
import {md5} from "../libs_drpy/crypto-util.js";
7+
import {PythonShell, PythonShellError} from 'python-shell';
8+
import {fastify} from "../controllers/fastlogger.js";
49

10+
// 缓存已初始化的模块和文件 hash 值
511
const moduleCache = new Map();
6-
7-
8-
const getRule = async function (filePath, env) {
9-
const moduleObject = await init(filePath, env);
10-
return JSON.stringify(moduleObject);
11-
}
12+
const ruleObjectCache = new Map();
13+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
14+
const _data_path = path.join(__dirname, '../data');
15+
const _config_path = path.join(__dirname, '../config');
16+
const _lib_path = path.join(__dirname, '../spider/py');
17+
const timeout = 30000; // 30秒超时
1218

1319
const json2Object = function (json) {
20+
// console.log('json2Object:', json);
1421
if (!json) {
1522
return {}
1623
} else if (json && typeof json === 'object') {
@@ -19,23 +26,147 @@ const json2Object = function (json) {
1926
return JSON.parse(json);
2027
}
2128

29+
const loadEsmWithHash = async function (filePath, fileHash, env) {
30+
// 创建Python模块代理
31+
const spiderProxy = {};
32+
const bridgePath = path.join(_lib_path, '_bridge.py'); // 桥接脚本路径
33+
34+
// 创建方法调用函数
35+
const callPythonMethod = async (methodName, ...args) => {
36+
37+
const options = {
38+
mode: 'text', // 使用JSON模式自动解析
39+
pythonOptions: ['-u'], // 无缓冲输出
40+
// scriptPath: path.dirname(bridgePath),
41+
timeout: timeout,
42+
env: {
43+
"PYTHONIOENCODING": 'utf-8',
44+
}
45+
};
46+
// 将参数序列化为JSON字符串
47+
const jsonArgs = args.map(arg => JSON.stringify(arg));
48+
console.log(methodName, ...jsonArgs);
49+
try {
50+
const results = await PythonShell.run(bridgePath, {
51+
...options,
52+
args: [filePath, methodName, ...jsonArgs]
53+
});
54+
// 取最后一条返回
55+
let vodResult = results.slice(-1)[0];
56+
// if (methodName !== 'init') {
57+
// console.log(vodResult);
58+
// }
59+
if (typeof vodResult === 'string' && vodResult) {
60+
switch (vodResult) {
61+
case 'None':
62+
vodResult = null;
63+
break;
64+
case 'True':
65+
vodResult = true;
66+
break;
67+
case 'False':
68+
vodResult = false;
69+
break;
70+
default:
71+
vodResult = JSON5.parse(vodResult);
72+
break;
73+
74+
}
75+
}
76+
// console.log('hipy logs:', results.slice(0, -1));
77+
fastify.log.info(`hipy logs: ${JSON.stringify(results.slice(0, -1))}`);
78+
// fastify.log.info(`typeof vodResult: ${typeof vodResult}`);
79+
// console.log('vodResult:', vodResult);
80+
// 检查是否有错误
81+
if (vodResult && vodResult.error) {
82+
throw new Error(`Python错误: ${vodResult.error}\n${vodResult.traceback}`);
83+
}
84+
85+
return vodResult; // 返回最后1个结果集
86+
} catch (error) {
87+
console.error('error:', error);
88+
if (error instanceof PythonShellError) {
89+
// 尝试解析Python的错误输出
90+
try {
91+
const errorData = JSON.parse(error.message);
92+
throw new Error(`Python错误: ${errorData.error}\n${errorData.traceback}`);
93+
} catch (e) {
94+
throw new Error(`Python执行错误: ${error.message}`);
95+
}
96+
}
97+
throw error;
98+
}
99+
};
100+
101+
// 定义Spider类的方法
102+
const spiderMethods = [
103+
'init', 'home', 'homeVod', 'homeContent', 'category',
104+
'detail', 'search', 'play', 'proxy', 'action', 'initEnv'
105+
];
106+
107+
// 为代理对象添加方法
108+
spiderMethods.forEach(method => {
109+
spiderProxy[method] = async (...args) => {
110+
return callPythonMethod(method, ...args);
111+
};
112+
});
113+
114+
// 处理initEnv调用
115+
if (typeof spiderProxy.initEnv === 'function' && env) {
116+
await spiderProxy.initEnv(env);
117+
}
118+
119+
return spiderProxy;
120+
}
121+
122+
const getRule = async function (filePath, env) {
123+
const moduleObject = await init(filePath, env);
124+
return JSON.stringify(moduleObject);
125+
}
126+
22127
const init = async function (filePath, env = {}, refresh) {
23128
try {
129+
// console.log('execute init');
24130
const fileContent = await readFile(filePath, 'utf-8');
25131
const fileHash = computeHash(fileContent);
132+
const moduleName = path.basename(filePath, '.js');
26133
let moduleExt = env.ext || '';
134+
const default_init_cfg = {
135+
stype: 4, //T3/T4 源类型
136+
skey: `hipy_${moduleName}`,
137+
sourceKey: `hipy_${moduleName}`,
138+
ext: moduleExt,
139+
};
140+
let SitesMap = getSitesMap(_config_path);
141+
if (moduleExt && SitesMap[moduleName]) {
142+
try {
143+
moduleExt = ungzip(moduleExt);
144+
} catch (e) {
145+
log(`[${moduleName}] ungzip解密moduleExt失败: ${e.message}`);
146+
}
147+
if (!SitesMap[moduleName].find(i => i.queryStr === moduleExt) && !SitesMap[moduleName].find(i => i.queryObject.params === moduleExt)) {
148+
throw new Error("moduleExt is wrong!")
149+
}
150+
}
27151
let hashMd5 = md5(filePath + '#pAq#' + moduleExt);
28152
if (moduleCache.has(hashMd5) && !refresh) {
29153
const cached = moduleCache.get(hashMd5);
30-
if (cached.hash === fileHash) {
154+
// 除hash外还必须保证proxyUrl实时相等,避免本地代理url的尴尬情况
155+
if (cached.hash === fileHash && cached.proxyUrl === env.proxyUrl) {
31156
return cached.moduleObject;
32157
}
33158
}
34159
log(`Loading module: ${filePath}`);
35-
let rule = {};
36-
await rule.init(moduleExt || {});
160+
let t1 = getNowTime();
161+
let module;
162+
module = await loadEsmWithHash(filePath, fileHash, env);
163+
// console.log('module:', module);
164+
const rule = module;
165+
await rule.init(default_init_cfg);
166+
let t2 = getNowTime();
37167
const moduleObject = deepCopy(rule);
38-
moduleCache.set(hashMd5, {moduleObject, hash: fileHash});
168+
moduleObject.cost = t2 - t1;
169+
moduleCache.set(hashMd5, {moduleObject, hash: fileHash, proxyUrl: env.proxyUrl});
39170
return moduleObject;
40171
} catch (error) {
41172
console.log(`Error in hipy.init :${filePath}`, error);
@@ -50,11 +181,12 @@ const home = async function (filePath, env, filter = 1) {
50181

51182
const homeVod = async function (filePath, env) {
52183
const moduleObject = await init(filePath, env);
53-
return json2Object(await moduleObject.homeVod());
184+
const homeVodResult = json2Object(await moduleObject.homeVod());
185+
return homeVodResult && homeVodResult.list ? homeVodResult.list : homeVodResult;
54186
}
55187

56188

57-
const cate = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
189+
const category = async function (filePath, env, tid, pg = 1, filter = 1, extend = {}) {
58190
const moduleObject = await init(filePath, env);
59191
return json2Object(await moduleObject.category(tid, pg, filter, extend));
60192
}
@@ -91,7 +223,7 @@ export default {
91223
init,
92224
home,
93225
homeVod,
94-
cate,
226+
category,
95227
detail,
96228
search,
97229
play,

0 commit comments

Comments
 (0)