Skip to content

Commit f93d0f4

Browse files
author
Taois
committed
fix: 修复linux环境下插件由于权限问题导致启动失败问题
1 parent 1e9d9bb commit f93d0f4

File tree

5 files changed

+169
-26
lines changed

5 files changed

+169
-26
lines changed

index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ fastify.addHook('onRequest', async (req, reply) => {
9595
// 如果需要,可以在这里对 req.query 进行进一步处理
9696
});
9797

98+
process.on("uncaughtException", (err) => {
99+
console.error("未捕获异常:", err);
100+
// 不退出,让主进程继续跑
101+
});
102+
98103
process.on('unhandledRejection', (err) => {
99104
fastify.log.error(`未处理的Promise拒绝:${err.message}`);
100105
console.log(`发生了致命的错误,已阻止进程崩溃。${err.stack}`);

spider/js/追剧狂人.js

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/*
2+
@header({
3+
searchable: 1,
4+
filterable: 1,
5+
quickSearch: 1,
6+
title: '追剧狂人',
7+
logo: 'https://i-blog.csdnimg.cn/blog_migrate/2621e710a94ab40ba66645d47f296aaf.gif',
8+
lang: 'ds'
9+
})
10+
*/
11+
12+
var rule = {
13+
类型: "影视",
14+
title: "追剧狂人",
15+
author: "不告诉你",
16+
logo: 'https://i-blog.csdnimg.cn/blog_migrate/2621e710a94ab40ba66645d47f296aaf.gif',
17+
host: "https://www.zjkrmv.vip",
18+
url: "/vodshow/fyfilter.html",
19+
searchUrl: "/vodsearch/**----------fypage---",
20+
searchable: 1, quickSearch: 1, double: true, timeout: 10000, play_parse: true, filterable: 1, invalid: true,
21+
class_name: "电影&连续剧&动漫&综艺&短剧",
22+
class_url: "1&2&4&3&23",
23+
filter_url: '{{fl.类型}}-{{fl.地区}}-{{fl.by}}-{{fl.剧情}}-----fypage---{{fl.年份}}.html',
24+
filter_def: {1: {类型: '1'}, 2: {类型: '2'}, 3: {类型: '3'}, 4: {类型: '4'}, 23: {类型: '23'}},
25+
推荐: async function (tid, pg, filter, extend) {
26+
const {input, pdfa, pdfh, pd} = this;
27+
const html = await request(input);
28+
const d = [];
29+
const data = pdfa(html, ".myui-vodbox-content");
30+
data.forEach((it) => {
31+
d.push({
32+
title: pdfh(it, ".title&&Text"),
33+
pic_url: pd(it, "img&&src"),
34+
desc: pdfh(it, ".tag&&Text"),
35+
url: pd(it, "a&&href"),
36+
});
37+
});
38+
return setResult(d);
39+
},
40+
一级: async function (tid, pg, filter, extend) {
41+
const {input, pdfa, pdfh, pd} = this;
42+
const html = await request(input);
43+
const d = [];
44+
const data = pdfa(html, ".show-vod-list&&a");
45+
data.forEach((it) => {
46+
d.push({
47+
title: pdfh(it, ".title&&Text"),
48+
pic_url: pd(it, "img&&src"),
49+
desc: pdfh(it, ".tag&&Text"),
50+
url: pd(it, "a&&href"),
51+
});
52+
});
53+
return setResult(d);
54+
},
55+
二级: async function (ids) {
56+
const {input, pdfa, pdfh, pd} = this;
57+
const html = await request(input);
58+
const playlist = pdfa(html, ".tab-pane");
59+
const tabs = pdfa(html, ".player-box&&ul li");
60+
let playmap = {};
61+
tabs.map((item, i) => {
62+
const form = pdfh(item, "Text");
63+
const list = playlist[i];
64+
const a = pdfa(list, "body&&a:not(:contains(排序))");
65+
a.map((it) => {
66+
let title = pdfh(it, "Text");
67+
let urls = pd(it, "a&&href", input);
68+
if (!playmap.hasOwnProperty(form)) {
69+
playmap[form] = [];
70+
}
71+
playmap[form].push(title + "$" + urls);
72+
});
73+
});
74+
const urls = Object.values(playmap);
75+
const playUrls = urls.map((urllist) => urllist.join("#"));
76+
const VOD = {
77+
vod_name: pdfh(html, "h1&&Text"), // 名称
78+
vod_actor: pdfh(html, ".director:eq(1)&&Text"), // 演员
79+
vod_director: pdfh(html, ".director:eq(0)&&Text"), // 导演
80+
vod_remarks: pdfh(html, ".bottom:eq(1)&&Text"), // 备注
81+
vod_content: pdfh(html, ".wrapper_more_text&&Text"), // 简介p:eq(0)&&Text
82+
vod_play_from: Object.keys(playmap).join("$$$"), // 线路
83+
vod_play_url: playUrls.join("$$$"), // 播放地址
84+
};
85+
return VOD;
86+
},
87+
搜索: async function (wd, quick, pg) {
88+
const homeFn = rule.一级.bind(this);
89+
return await homeFn();
90+
},
91+
lazy: async function (flag, id, flags) {
92+
let {input, pdfa, pdfh, pd} = this;
93+
let html = await request(input);
94+
html = JSON.parse(html.match(/r player_.*?=(.*?)</)[1]);
95+
let url = html.url;
96+
if (html.encrypt == "1") {
97+
url = unescape(url)
98+
return {parse: 0, url: url}
99+
} else if (html.encrypt == "2") {
100+
url = unescape(base64Decode(url))
101+
return {parse: 0, url: url}
102+
}
103+
if (/m3u8|mp4/.test(url)) {
104+
input = url
105+
return {parse: 0, url: input}
106+
} else {
107+
return {parse: 1, url: input}
108+
}
109+
},
110+
filter: "H4sIAAAAAAAAA+1aXW/TSBR951dUfu5D7X7CX1nxwK4qLdqFlRZ2JYSQgDRpk7JJWtFAaPqlbT4oDU2A7bYOSf5MZpz8CyZzJ547ae/ESBSBcJ96fMae4+vre8948vDalPpzXOfG1E8PQyyP/bb8QBx1gmaL7a470yZ599adZZr9+9bvfy3LSzp3h6NYsjZI1JxpwQyh6zyanhpRmVqvXQrSqyG7gNlCiaWrmF2UrDEdCJJq1po8kZTDL46Qpy85BqFnCqob7LyFZ7qOdPAnef64gFl3BstMV0fzjmjXppOvvez5aZtO16OEioAFzw2hLlbKyqtj9+FhpfzpcVDYMGir0sA/Yu0tm1JvXOnNEF647iinWKnBnvlUTl3OTsgpHILD6qCYChkFNT+oFPnZScgraAkCyzX4eYeMgKKpxOpk2XY7nE1B9ETeb2FeQc3315uYV9CWXC/KvHRMqlU0oXaw9xrPpiCKbed/zCuI76Zh3k1jglr2T4P5FTq2QBNq+UpOPF229lpPGB5BESx3g1w9SBd1EMMjNmWZfb7eFVejxYUjqHc1edprFXS0AEZ6Q84/9Fpt8g25lI3+hngz3jyqDwIY3Bzm5mwxEvwsXRmGJBEZwXl4Fs9U4GLONbkZzM3Y1YnSSKsTJKnOXUKzCGBwi5hbNLkFzC1MUDdvUzdvUYefkACRMkr2KDKjLmU/o+bKRq0zHSCqgrIz6yoI0FbFZMelqxjQdI8UpkKrATje7LUagOP9XJ8PcGJDp9UCTajt1yvs2ZEuTgCR2sxJ0Na8guOOAN/tENoqm/QIdFkDmqq59Uqvs68LLkCkJrU5KOqCrCA6f/dIhEOfD/CqMgH8jp4NIOITGf70leYBoti3cix5pmMP0KJ2sLPJX5ZJtYqm1BZW+2lfqwGIu+/b4Pl/rN1EDXh0BI3KHfb/RdkL0JYP2RTLvaPzAWgqe7t5kXE6ewHifCjxnQ2UDxIifqUbvClrHqDNkrY3glZpFAdCszHos/3pzw+oOsmzm8zPTaqTvNDonWUCvxve1v3b4nyUVok6r+gC9evt+/cMD9/un6yE7L1f/vhz2ajxN/X8jud8zaUb7tPC2fX8Ki777ix+tc5OhDM2aKuXEMZ0aDfTtN+iWyI/rg6NNZ5rIVpXvNqVSCopLoFyX8IobpuqLmrpQlUXoKmuqJYx5rIFBVEtHMyFQpSVDPVE1WKAqtxAU3WFXPeE75jw32znAJXL0ZHYgccO/MaP5sDJrm9aVNOSTnQy7MkpS4w3PB0lYxBVdyasDdjbU+bXNQ8wiv8lV+5g/8llu6Qptcocm2Y4ytrANFOmebL5GWUxKScDNKG2d36A3ZWC2N8meVF/61IQqd1KYTeuoN192Vq0or/495DvyZHNxo4sdmSxI4sd2SURjh1Z7MhiRzauOXZksSO7Skc291Ud2dxFR5ap8Y9vdH1aiuC61ClULSTrtDJ55oyzxm8AwJqNjXC/BXcmo6WzEWAUP0TFUt4q7XeA/uLvRuxUYqfyPTmVYRNa2TN6koCoFJP7pVRPStT7B4/pngQ0VcGytSCvf7+jIOLzu8Ex2tMDiNd41A4kobaf3+ln6d1bRVNq9w/YtvZKCiI1E/aieck39kMB2tSqDTdKLdCUWtJzKrUN8Wg/aLUATb783uAFtGXCYaf38RWdCUBTFTi7x9a2kXOTEHkt/x2r57XXAojydjvDi3o/VUGrT22y7guLT5X0D+2lPMvnrW/+PuS/jz4Bwn2c1vkqAAA="
111+
}

spider/py/core/t4_daemon.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -726,6 +726,7 @@ def _stop(*_):
726726
_manager.stop()
727727
# 让 serve_forever() 退出
728728
srv.shutdown()
729+
logger.info("The service has successfully exited")
729730
sys.exit(0) # 保证退出码是 0
730731

731732
if os.name == "posix":

spider/py/core/t4_daemon_lite.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,7 @@ def _stop(*_):
479479
_manager.stop()
480480
# 让 serve_forever() 退出
481481
srv.shutdown()
482+
logger.info("The service has successfully exited")
482483
sys.exit(0) # 保证退出码是 0
483484

484485
if os.name == "posix":

utils/pluginManager.js

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,22 @@ function getPluginBinary(rootDir, pluginPath, pluginName) {
5454
return path.join(binDir, binaryName);
5555
}
5656

57+
function ensureExecutable(filePath) {
58+
if (process.platform === "win32") {
59+
// Windows 不需要 chmod,直接返回
60+
return;
61+
}
62+
try {
63+
const stats = fs.statSync(filePath);
64+
if (!(stats.mode & 0o111)) {
65+
fs.chmodSync(filePath, 0o755);
66+
console.log(`[pluginManager] 已为插件 ${filePath} 添加执行权限`);
67+
}
68+
} catch (err) {
69+
console.error(`[pluginManager] 无法设置执行权限: ${filePath}`, err.message);
70+
}
71+
}
72+
5773
/**
5874
* 启动插件
5975
* @param {Object} plugin 插件配置
@@ -77,37 +93,46 @@ function startPlugin(plugin, rootDir) {
7793
let proc;
7894

7995
try {
80-
proc = spawn(binary, args, {
81-
stdio: ["ignore", "pipe", "pipe"],
82-
});
83-
} catch (err) {
84-
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (spawn 出错):`, err.message);
85-
return null;
86-
}
87-
88-
// 检查是否真的启动了
89-
if (!proc || !proc.pid) {
90-
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (无效的进程 PID)`);
91-
return null;
92-
}
96+
ensureExecutable(binary);
97+
// 用 pipe 方式,便于我们捕获插件日志
98+
proc = spawn(binary, args, {stdio: ["ignore", "pipe", "pipe"]});
99+
100+
// 检查是否真的启动了
101+
if (!proc || !proc.pid) {
102+
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (无效的进程 PID)`);
103+
return null;
104+
}
93105

94-
proc.stdout.on("data", (data) => {
95-
console.log(`[${plugin.name}]`, data.toString().trim());
96-
});
106+
proc.stdout.on("data", (data) => {
107+
console.log(`[${plugin.name}]`, data.toString().trim());
108+
});
97109

98-
proc.stderr.on("data", (data) => {
99-
console.error(`[${plugin.name}-STD]`, data.toString().trim());
100-
});
110+
proc.stderr.on("data", (data) => {
111+
console.log(`[${plugin.name}-STD]`, data.toString().trim());
112+
});
101113

102-
proc.on("exit", (code, signal) => {
103-
console.log(`[pluginManager] 插件 ${plugin.name} 退出 (code=${code}, signal=${signal})`);
104-
});
114+
proc.on("error", (err) => {
115+
if (err.code === "EACCES") {
116+
console.error(`[pluginManager] 插件 ${plugin.name} 无法执行: 没有执行权限,请运行: chmod +x ${binary}`);
117+
} else if (err.code === "ENOENT") {
118+
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败: 找不到可执行文件 ${binary}`);
119+
} else {
120+
console.error(`[pluginManager] 插件 ${plugin.name} 运行中出错:`, err.message);
121+
}
122+
// 标记为“启动失败”,避免 exit 再重复打印
123+
proc._failedToSpawn = true;
124+
});
105125

106-
proc.on("error", (err) => {
107-
console.error(`[pluginManager] 插件 ${plugin.name} 运行中出错:`, err.message);
108-
});
126+
proc.on("exit", (code, signal) => {
127+
if (proc._failedToSpawn) return; // 忽略 spawn 失败导致的 exit
128+
console.log(`[pluginManager] 插件 ${plugin.name} 退出 (code=${code}, signal=${signal})`);
129+
});
109130

110-
return proc;
131+
return proc;
132+
} catch (err) {
133+
console.error(`[pluginManager] 插件 ${plugin.name} 启动失败 (spawn 出错):`, err.message);
134+
return null;
135+
}
111136
}
112137

113138
/**

0 commit comments

Comments
 (0)