crabatool 1.0.845 → 1.0.847

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,354 @@
1
+ # crabatool 智能助手使用指南
2
+
3
+ ## 1. 工具简介
4
+
5
+ crabatool 是一个基于 Node.js 的前端工程化脚手架工具,主要服务于 Craba/NGP 前端项目体系。支持命令行(CLI)方式使用,兼容 Windows、macOS、Linux,可集成 Jenkins 实现自动化构建。
6
+
7
+ **核心价值**:
8
+ - 快速搭建 Craba 项目环境
9
+ - 提供本地开发服务器,支持热刷新
10
+ - 代码质量检测与优化
11
+ - 自动化构建与部署
12
+ - 知识库管理与更新
13
+
14
+ ## 2. 环境要求与安装
15
+
16
+ ### 2.1 环境要求
17
+ - **Node.js**:LTS 版本
18
+ - 支持平台:Windows、macOS、Linux
19
+
20
+ ### 2.2 安装方法
21
+
22
+ **全局安装(推荐)**:
23
+ ```bash
24
+ npm install crabatool -g
25
+ ```
26
+
27
+ **从内网仓库安装(更快)**:
28
+ ```bash
29
+ npm install crabatool -g --registry http://172.17.0.236:4873/
30
+ ```
31
+
32
+ **验证安装**:
33
+ ```bash
34
+ # 查看版本号
35
+ crabatool -v
36
+
37
+ # 查看帮助信息
38
+ crabatool --help
39
+ ```
40
+
41
+ ## 3. 核心功能与使用示例
42
+
43
+ ### 3.1 项目初始化与环境搭建
44
+
45
+ **功能说明**:快速创建和配置 Craba 项目环境
46
+
47
+ **使用示例**:
48
+ ```bash
49
+ # 第一步:创建项目目录
50
+ mkdir my-craba-app && cd my-craba-app
51
+
52
+ # 第二步:安装 Craba 框架
53
+ crabatool -install -webPath .
54
+
55
+ # 第三步:创建基础文件
56
+ # 使用智能助手或大模型生成 index.html、Main.gspx 等必要文件
57
+
58
+ # 第四步:启动开发服务器
59
+ crabatool -run -webPath . -port 8080
60
+
61
+ # 第五步:打开浏览器访问
62
+ # http://localhost:8080
63
+ ```
64
+
65
+ ### 3.2 本地开发服务器
66
+
67
+ **功能说明**:启动基于 Express 的本地前端静态资源服务器,支持自动打开浏览器、文件热刷新、代理转发、gzip 压缩等
68
+
69
+ **参数说明**:
70
+ - `-webPath`:网站根路径(必填)
71
+ - `-port`:前端服务端口号(必填)
72
+ - `-refresh true`:开启文件变动自动刷新浏览器
73
+ - `-proxy`:配置 API 代理转发规则
74
+
75
+ **使用示例**:
76
+ ```bash
77
+ # 基本启动
78
+ crabatool -run -webPath F:\myproject\www -port 9090
79
+
80
+ # 启动并开启热刷新
81
+ crabatool -run -webPath F:\myproject\www -port 9090 -refresh true
82
+
83
+ # 启动并配置代理转发
84
+ crabatool -run -webPath F:\myproject\www -port 9090 -proxy "{'/apis/':{'target':'http://127.0.0.1:8082','changeOrigin':true}}"
85
+ ```
86
+
87
+ ### 3.3 代码质量检测
88
+
89
+ **功能说明**:对项目下所有 JS 文件进行语法兼容性检测,包括 ESLint 规则检查、编码检测、文件大小检测、ES6+ 语法检测,同时检查 gspx 文件中的标签使用规范
90
+
91
+ **参数说明**:
92
+ - `-webPath`:项目路径(必填)
93
+ - `-modName`:项目(模块)名称
94
+ - `-ignoreCheck`:忽略检测的目录或文件
95
+ - `-globals`:全局变量名列表
96
+
97
+ **使用示例**:
98
+ ```bash
99
+ # 基本检测
100
+ crabatool -checkjs -webPath F:\myproject\www -modName mymod
101
+
102
+ # 忽略特定目录和文件
103
+ crabatool -checkjs -webPath F:\myproject\www -modName mymod -ignoreCheck ueditor,iconfont,js/math.min.js
104
+ ```
105
+
106
+ ### 3.4 框架更新与管理
107
+
108
+ **功能说明**:从内网服务器拉取最新的 Craba 平台资源,覆盖到指定项目路径下
109
+
110
+ **参数说明**:
111
+ - `-webPath`:项目路径(必填)
112
+ - `-version`:指定分支版本号,默认为 `master`
113
+
114
+ **使用示例**:
115
+ ```bash
116
+ # 更新主分支平台
117
+ crabatool -update -webPath F:\myproject\www
118
+
119
+ # 更新指定分支
120
+ crabatool -update -webPath F:\myproject\www -version ngp5.3
121
+ ```
122
+
123
+ ### 3.5 知识库管理
124
+
125
+ **功能说明**:上传和更新 Craba 知识库文档
126
+
127
+ **参数说明**:
128
+ - `-docsPath`:文档路径(必填)
129
+
130
+ **使用示例**:
131
+ ```bash
132
+ # 上传知识库到服务器
133
+ crabatool -uploadDocs -docsPath F:\craba-doc
134
+
135
+ # 从服务器更新知识库
136
+ crabatool -updateDocs -docsPath F:\craba-doc
137
+ ```
138
+
139
+ ### 3.6 版本管理
140
+
141
+ **功能说明**:查看版本信息和管理版本号
142
+
143
+ **使用示例**:
144
+ ```bash
145
+ # 查看 crabatool 工具版本
146
+ crabatool -v
147
+
148
+ # 查看 Craba 框架的最新版本
149
+ crabatool -crabaVersion
150
+
151
+ # 自动添加版本号(时间戳)
152
+ crabatool -addVersion -file F:\project\www\index.html -exp AUTOVERSION
153
+ ```
154
+
155
+ ## 4. 高级功能
156
+
157
+ ### 4.1 文件合并与压缩
158
+
159
+ **功能说明**:将多个 JS 或 CSS 文件合并为一个文件,支持压缩
160
+
161
+ **使用示例**:
162
+ ```bash
163
+ # 合并压缩 JS 文件
164
+ crabatool -mergejs -targetPath F:\project\js -inNames a.js,b.js -outName F:\output\merged.js
165
+
166
+ # 合并压缩 CSS 文件
167
+ crabatool -mergecss -targetPath F:\project\skins -inNames iconfont.css,craba.min.css -outName D:\output\craba.min.css
168
+
169
+ # 打包资源文件为 JSON
170
+ crabatool -mergeFiles -modName shell -webPath F:\project\shell -targetPath F:\project\shell -inNames skins/craba.min.css,Main.gspx,Main.js,js/init.js -outName F:\project\shell\shell.res.js
171
+ ```
172
+
173
+ ### 4.2 开发环境配置
174
+
175
+ **功能说明**:自动安装或更新 IDE 开发环境配置
176
+
177
+ **使用示例**:
178
+ ```bash
179
+ # 安装/更新 IDEA 环境
180
+ crabatool -idea
181
+
182
+ # 安装/更新 VSCode 环境
183
+ crabatool -vscode
184
+ ```
185
+
186
+ ### 4.3 图标字体管理
187
+
188
+ **功能说明**:检测项目中图标字体的使用情况,解压并部署字体库文件
189
+
190
+ **使用示例**:
191
+ ```bash
192
+ # 检测图标字体使用情况
193
+ crabatool -checkiconfont -webPath F:\myproject\www -modName mymod
194
+
195
+ # 解压并部署字体库
196
+ crabatool -iconfont -zipPath F:\project\biconfont -fontPath F:\project\skins\bicon -prefix .bicon- -fontName biconfont
197
+ ```
198
+
199
+ ## 5. 命令行参数速查
200
+
201
+ ### 5.1 通用参数
202
+
203
+ | 参数 | 说明 |
204
+ |------|------|
205
+ | `--skip-report` | 跳过系统信息上报,提高启动速度 |
206
+ | `-v` | 查看 crabatool 工具版本号 |
207
+ | `-crabaVersion` | 查看 Craba 框架的最新版本号 |
208
+
209
+ ### 5.2 核心功能参数
210
+
211
+ | 命令 | 说明 | 必填参数 |
212
+ |------|------|----------|
213
+ | `-install` | 安装 Craba 平台资源 | `-webPath` |
214
+ | `-run` | 启动本地前端服务器 | `-webPath`, `-port` |
215
+ | `-update` | 更新 Craba 平台资源 | `-webPath` |
216
+ | `-checkjs` | 检查项目 JS 语法兼容性 | `-webPath` |
217
+ | `-uploadDocs` | 上传 Craba 知识库 | `-docsPath` |
218
+ | `-updateDocs` | 更新 Craba 知识库 | `-docsPath` |
219
+ | `-mergejs` | 合并压缩 JS 文件 | `-inNames`, `-outName` |
220
+ | `-mergecss` | 合并压缩 CSS 文件 | `-inNames`, `-outName` |
221
+ | `-mergeFiles` | 打包资源文件为 JSON | `-inNames` |
222
+
223
+ ## 6. 智能助手集成指南
224
+
225
+ ### 6.1 与 Agent 集成
226
+
227
+ **使用场景**:
228
+ - 自动初始化项目环境
229
+ - 构建自动化工作流
230
+ - 代码质量检测与报告
231
+
232
+ **集成示例**:
233
+ ```javascript
234
+ // Agent 调用示例
235
+ async function initializeProject(projectPath, port = 8080) {
236
+ // 安装 Craba 框架
237
+ await execCommand(`crabatool -install -webPath ${projectPath}`);
238
+
239
+ // 创建基础文件(index.html、Main.gspx 等)
240
+ await createBasicFiles(projectPath);
241
+
242
+ // 启动开发服务器
243
+ await execCommand(`crabatool -run -webPath ${projectPath} -port ${port} -refresh true`);
244
+
245
+ return `项目已初始化并启动,访问地址: http://localhost:${port}`;
246
+ }
247
+
248
+ // 创建基础文件的辅助函数
249
+ async function createBasicFiles(projectPath) {
250
+ // 这里可以实现创建 index.html、Main.gspx 等必要文件的逻辑
251
+ // 例如使用模板生成或从默认模板复制
252
+ console.log(`在 ${projectPath} 中创建基础文件...`);
253
+ // 具体实现代码
254
+ }
255
+
256
+ // 代码质量检测
257
+ async function checkCodeQuality(projectPath, moduleName) {
258
+ const result = await execCommand(`crabatool -checkjs -webPath ${projectPath} -modName ${moduleName}`);
259
+ return `代码质量检测完成: ${result}`;
260
+ }
261
+ ```
262
+
263
+ ### 6.2 与大模型集成
264
+
265
+ **使用场景**:
266
+ - 生成项目脚手架
267
+ - 自动化代码审查
268
+ - 知识库管理与更新
269
+
270
+ **提示词模板**:
271
+ ```
272
+ 你是一个前端工程化专家,使用 crabatool 工具来管理 Craba 项目。
273
+
274
+ 请根据以下需求执行相应操作:
275
+ 1. 创建一个新的 Craba 项目,路径为 ${projectPath}
276
+ 2. 安装最新版本的 Craba 框架
277
+ 3. 创建基础文件(index.html、Main.gspx 等)
278
+ 4. 启动开发服务器,端口为 ${port}
279
+ 5. 检查项目代码质量
280
+
281
+ 请使用 crabatool 命令完成上述任务,并返回执行结果。
282
+ ```
283
+
284
+ ## 7. 最佳实践
285
+
286
+ ### 7.1 项目初始化流程
287
+
288
+ 1. **创建项目目录**:`mkdir my-project && cd my-project`
289
+ 2. **安装 Craba 框架**:`crabatool -install -webPath .`
290
+ 3. **创建基础文件**:使用 agent 或大模型生成 index.html、Main.gspx 等文件
291
+ 4. **启动开发服务器**:`crabatool -run -webPath . -port 8080 -refresh true`
292
+ 5. **开发与调试**:使用热刷新功能进行开发
293
+ 6. **代码检查**:`crabatool -checkjs -webPath . -modName myproject`
294
+ 7. **构建与部署**:合并压缩文件,添加版本号
295
+
296
+ ### 7.2 性能优化
297
+
298
+ - **开发环境**:使用 `-refresh` 启用热更新,使用 `-ignoreCompress` 不压缩代码
299
+ - **生产环境**:合并所有 JS 文件,压缩代码,添加版本号,生成 hash 清单
300
+
301
+ ### 7.3 常见问题与解决方案
302
+
303
+ **问题**:端口被占用
304
+ **解决方案**:使用其他端口,如 `crabatool -run -webPath . -port 3001`
305
+
306
+ **问题**:找不到模块
307
+ **解决方案**:重新安装 crabatool,`npm install crabatool -g`
308
+
309
+ **问题**:权限不足
310
+ **解决方案**:使用管理员权限运行命令提示符或终端
311
+
312
+ **问题**:npm 安装失败
313
+ **解决方案**:使用内网仓库,`npm install crabatool -g --registry http://172.17.0.236:4873/`
314
+
315
+ ## 8. 命令参考速查表
316
+
317
+ ### 8.1 常用命令
318
+
319
+ | 命令 | 功能 | 示例 |
320
+ |------|------|------|
321
+ | `-install` | 安装平台资源 | `crabatool -install -webPath .` |
322
+ | `-run` | 启动本地服务 | `crabatool -run -webPath . -port 8080` |
323
+ | `-update` | 更新平台资源 | `crabatool -update -webPath .` |
324
+ | `-checkjs` | JS 语法检测 | `crabatool -checkjs -webPath . -modName myapp` |
325
+ | `-v` | 查看工具版本 | `crabatool -v` |
326
+ | `-crabaVersion` | 查看框架版本 | `crabatool -crabaVersion` |
327
+ | `-uploadDocs` | 上传知识库 | `crabatool -uploadDocs -docsPath F:\craba-doc` |
328
+ | `-updateDocs` | 更新知识库 | `crabatool -updateDocs -docsPath F:\craba-doc` |
329
+
330
+ ### 8.2 高级命令
331
+
332
+ | 命令 | 功能 | 示例 |
333
+ |------|------|------|
334
+ | `-mergejs` | 合并压缩 JS | `crabatool -mergejs -inNames a.js,b.js -outName merged.js` |
335
+ | `-mergecss` | 合并压缩 CSS | `crabatool -mergecss -inNames a.css,b.css -outName merged.css` |
336
+ | `-mergeFiles` | 打包资源为 JSON | `crabatool -mergeFiles -inNames file1.js,file2.css -outName res.js` |
337
+ | `-addVersion` | 添加版本号 | `crabatool -addVersion -file index.html -exp AUTOVERSION` |
338
+ | `-idea` | 安装 IDEA 环境 | `crabatool -idea` |
339
+ | `-vscode` | 安装 VSCode 环境 | `crabatool -vscode` |
340
+ | `-checkiconfont` | 检测图标字体 | `crabatool -checkiconfont -webPath . -modName myapp` |
341
+ | `-iconfont` | 部署字体库 | `crabatool -iconfont -zipPath ./font -fontPath ./skins` |
342
+
343
+ ## 9. 总结
344
+
345
+ crabatool 是一个功能强大的前端工程化脚手架工具,通过命令行方式提供了丰富的功能,包括项目初始化、开发服务器、代码检测、文件合并、知识库管理等。与智能助手和大模型集成后,可以进一步提高开发效率,实现自动化工作流。
346
+
347
+ **核心优势**:
348
+ - 简单易用的命令行接口
349
+ - 丰富的功能集
350
+ - 跨平台支持
351
+ - 与智能助手和大模型的良好集成
352
+ - 适合从开发到部署的全流程使用
353
+
354
+ 通过本指南,智能助手和大模型可以快速掌握 crabatool 的使用方法,为前端开发提供更高效的工具支持。
@@ -0,0 +1,165 @@
1
+ # runEv 录屏功能实现文档
2
+
3
+ ## 1. 功能概述
4
+
5
+ `runEv` 是 crabatool 提供的录屏功能
6
+ 1. 用于录制教学视频和音频,支持自动检测录制完成并上传到七牛服务器,最终将音频 URL 返回给前端。
7
+ 2. 可提高录制教学人员的效率,减少繁琐人工关联教学步骤的音频还容易出错的问题。传统做法是先用ev录屏,然后给音频命名,然后教学采集界面去手工选择音频文件上传到七牛服务器,然后把音频url关联到教学里面。
8
+
9
+ ## 2. 实现架构
10
+
11
+ ### 2.1 系统架构
12
+
13
+ ```
14
+ ┌─────────────┐ WebSocket ┌─────────────┐ 本地文件系统 ┌─────────────┐
15
+ │ 前端页面 │────────────→│ crabatool │──────────────→│ ev 录屏工具 │
16
+ └─────────────┘ └─────────────┘ └─────────────┘
17
+ ↑ │
18
+ │ │
19
+ └──────────────────────────┘
20
+ 返回音频 URL
21
+ ```
22
+
23
+ ### 2.2 核心组件
24
+
25
+ - **前端**:`f:\crabaevery\www\local\study.plug.js` 中的 `doInsertPlayer` 函数调用独立封装的`evSocket`组件
26
+ - **后端**:`f:\crabatool\tool\evsockettool.js` 处理录屏逻辑
27
+ - **工具**:ev 录屏工具(自动检测路径)
28
+ - **存储**:七牛云存储(模拟实现)
29
+
30
+ ## 3. 实现步骤
31
+
32
+ ### 3.1 前端实现
33
+
34
+ 1. **连接 WebSocket**:
35
+ - **evSocket组件**:封装在`study.plug.js`文件底部,提供crabatool之间的消息处理,并支持doInsertPlayer传递的回调方法(业务处理一些界面更新和数据修改);且切换其他教学,关闭教学时要通知evSocket组件中断socket连接,并通知crabatool停止监听等操作,用户单独操作ev录屏不受影响。
36
+ - 使用固定端口 63096 连接到 crabatool 服务
37
+ - 处理连接成功、失败、关闭等事件
38
+ 2. **发送录屏请求**:
39
+ - 连接成功后发送 `evRecord` 消息,action 为 `start`
40
+ - 等待后端响应
41
+ 3. **处理响应**:
42
+ - `started`:显示录屏工具已启动的提示
43
+ - `completed`:获取音频 URL 并添加到当前步骤
44
+ - `error`:显示错误提示
45
+ 4. **资源清理**:
46
+ - 页面卸载时发送 `stop` 消息
47
+ - 连接关闭时发送 `stop` 消息
48
+
49
+ ### 3.2 后端实现
50
+
51
+ 1. **初始化**:
52
+ - 直接启动 runEv 服务时,什么都不用做
53
+ - 等待前端连接和 start 消息,如果等到的是连接消息,记录连接时间。
54
+ 2. **处理 start 消息**:
55
+ - 记录启动时间
56
+ - 自动检测 ev 录屏工具路径
57
+ - 启动 ev 录屏工具
58
+ - 创建文件监听器,监控输出目录
59
+ 3. **文件监控**:
60
+ - 监听文件 `change` 事件,文件创建时间必须大于启动时间
61
+ - 每隔3s检查文件创建时间和大小
62
+ 4. **录制完成检测**:
63
+ - 方案1:监控文件大小变化,连续 3 次(3 秒)大小稳定,如果3次检测发现文件大小有变动要重置计数因为ev还在录制中,且至少有过一次增长,认为录制完成。
64
+ - 方案2:固定3次3秒这种方式比较死板,可以根据文件大小变化规律,判断是否是录制完成。可以记录每次ev录屏过程的数据:从创建文件到每次更新文件的规律,包括文件大小、文件名、创建时间、更新时间等。
65
+ 5. **处理完成文件**:
66
+ - 模拟上传到七牛服务器
67
+ - 生成音频 URL
68
+ - 发送 `completed` 消息给前端
69
+ 6. **处理 stop 消息**:
70
+ - 停止录屏状态
71
+ - 清理监控文件
72
+ - 关闭文件监听器
73
+ 7. **日志处理**:
74
+ - crabatool所有关键步骤都要写日志,方便调试
75
+ - 日志封装log函数,需要增加日志时间。
76
+
77
+
78
+ ## 4. 技术思路
79
+
80
+ ### 4.1 自动检测 ev 路径
81
+
82
+ - 检查多个常见安装路径:
83
+ - `C:\Program Files\EVCapture\EVCapture.exe`
84
+ - `C:\Program Files (x86)\EVCapture\EVCapture.exe`
85
+ - `%LocalAppData%\EVCapture\EVCapture.exe`
86
+ - `%AppData%\EVCapture\EVCapture.exe`
87
+ - `%USERPROFILE%\Desktop\EVCapture.exe`
88
+ - 环境变量 `EV_PATH`
89
+
90
+ ### 4.3 错误处理
91
+
92
+ - 捕获文件操作错误
93
+ - 处理连接错误
94
+ - 提供详细的日志信息
95
+
96
+ ## 5. 使用方法
97
+
98
+ ### 5.1 启动服务
99
+
100
+ ```bash
101
+ # 基本启动
102
+ crabatool -runEv -evSavePath "ev保存音频的路径,如c:\\Users\\grasp\\ev recordings\\"
103
+ ```
104
+
105
+ ### 5.2 前端操作
106
+
107
+ 1. 打开教学录制页面
108
+ 2. 点击"采集这一步的音/视频"按钮
109
+ 3. 等待录屏工具启动
110
+ 4. 手动开始录制
111
+ 5. 录制完成后,音频会自动添加到当前步骤
112
+
113
+ ## 6. 常见问题及解决方案
114
+
115
+ ### 6.1 连接失败
116
+
117
+ **问题**:前端显示"连接crabatool服务失败"
118
+
119
+ **解决方案**:
120
+
121
+ - 确保 crabatool 服务已启动
122
+ - 检查端口是否被占用
123
+ - 检查网络连接
124
+
125
+ ### 6.3 连续录制问题
126
+
127
+ **问题**:连续录制多个音频时,后续录制没有被捕获
128
+
129
+ **解决方案**:
130
+
131
+ - 确保服务保持运行状态
132
+ - 每次录制完成后,socket会保持前端的通信状态
133
+ - 等待下一次录制的消息
134
+
135
+ ## 7. 代码结构
136
+
137
+ ### 7.1 前端代码
138
+
139
+ - `f:\crabaevery\www\local\study.plug.js`:
140
+ - `doInsertPlayer` 函数:处理插入播放器逻辑
141
+ - WebSocket 连接和消息处理
142
+ - 页面卸载时的资源清理
143
+
144
+ ### 7.2 后端代码
145
+
146
+ - `f:\crabatool\tool\evsockettool.js`:
147
+ - `handleEvRecord`:处理前端消息
148
+ - `startEvRecording`:启动录屏
149
+ - `stopEvRecording`:停止录屏
150
+ - `watchEvOutput`:监控输出目录
151
+ - `startMonitoringFile`:监控文件大小变化
152
+ - `processEvFile`:处理完成的文件
153
+ - `f:\crabatool\tool\sockettool.js`:
154
+ - 引入 `evsockettool.js`
155
+ - 处理 WebSocket 消息
156
+ - `f:\crabatool\tool\start.js`:
157
+ - `runEvServer`:启动 ev 录屏服务
158
+ - `f:\crabatool\index.js`:
159
+ - 添加 `-runEv` 命令支持
160
+
161
+ ## 8. 日志系统
162
+
163
+ - 所有日志都包含时间戳
164
+ - 详细记录文件监控、大小变化等信息
165
+ - 错误信息清晰明了
package/index.js CHANGED
@@ -334,6 +334,13 @@ async function checkFast(args) {
334
334
  return false;
335
335
  }
336
336
 
337
+ // 启动ev录屏服务
338
+ if (args.includes('-runEv')) {
339
+ start.bindConfigByArgv('-evSavePath');
340
+ start.bindConfigByArgv('-port');
341
+ start.runEvServer();
342
+ return false;
343
+ }
337
344
 
338
345
  return true;
339
346
  }
package/lib/config.js CHANGED
@@ -34,6 +34,7 @@ class Config {
34
34
 
35
35
  // 文件监听与代码检测相关配置
36
36
  this.enableFileWatcher = true; // 是否启用文件监听功能
37
+ this.evSavePath = ''; // ev录屏工具保存路径
37
38
 
38
39
  // 分离式更新相关配置
39
40
  this.enableDetachedUpdate = true; // 是否启用分离式更新
package/lib/server.js CHANGED
@@ -35,7 +35,29 @@ exports.run = function(options) {
35
35
  exports.start = function(options) {
36
36
  var app = new express();
37
37
  createServer(app, options);
38
+ }
38
39
 
40
+ // 2. 独立server服务(crabatool使用)
41
+ exports.startEvServer = function(options) {
42
+ var app = new express();
43
+ // 确保 port 是数字类型,避免字符串拼接导致端口超出范围
44
+ const portNum = parseInt(config.port, 10);
45
+ const socketPortNum = portNum;
46
+
47
+ // 检查计算后的端口是否在有效范围内 (0-65535)
48
+ if (socketPortNum > 65535) {
49
+ // 如果超出范围,使用一个较小的偏移量或固定端口
50
+ config.socketPort = portNum + 1000;
51
+ if (config.socketPort > 65535) {
52
+ // 如果还是超出,使用默认的 WebSocket 端口
53
+ config.socketPort = 8080;
54
+ }
55
+ utils.log(`警告: 计算的 socketPort (${socketPortNum}) 超出有效范围,已调整为 ${config.socketPort}`);
56
+ } else {
57
+ config.socketPort = socketPortNum;
58
+ }
59
+
60
+ require('../tool/sockettool.js');
39
61
  }
40
62
 
41
63
  // 3. 返回在线预览案例server总数
@@ -160,6 +182,7 @@ function createServer(app, options) {
160
182
 
161
183
  app.webPath = webPath; // handler.js里面动态获取当前app的网站路径(多个server实例的时候需要)
162
184
 
185
+ // 静态文件服务
163
186
  app.use(express.static(webPath, {
164
187
  maxage: '100h', // 强制缓存资源
165
188
  setHeaders: function(res, pathname, stat) {
@@ -180,6 +203,8 @@ function createServer(app, options) {
180
203
  }
181
204
  }));
182
205
 
206
+
207
+
183
208
  // 业务ap的代理转发
184
209
  if (config.proxy) {
185
210
  var { createProxyMiddleware } = require('http-proxy-middleware');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "crabatool",
3
- "version": "1.0.845",
3
+ "version": "1.0.847",
4
4
  "description": "crabatool",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -0,0 +1,351 @@
1
+ var utils = require('../lib/utils.js');
2
+ var config = require('../lib/config.js');
3
+ var path = require('path');
4
+ var chokidar = require('chokidar');
5
+ var fs = require('fs');
6
+ var childProcess = require('child_process');
7
+
8
+ // ev录屏工具相关配置
9
+ var evConfig = {
10
+ outputDir: config.evSavePath || process.env.UserProfile,
11
+ recording: false,
12
+ watcher: null,
13
+ conn: null,
14
+ startTime: null,
15
+ connectTime: null
16
+ };
17
+
18
+ // 监控的文件
19
+ var monitoringFiles = {};
20
+
21
+ // 确保输出目录存在
22
+ if (!fs.existsSync(evConfig.outputDir)) {
23
+ fs.mkdirSync(evConfig.outputDir, { recursive: true });
24
+ }
25
+
26
+ // 封装日志工具方法
27
+ function log() {
28
+ var args = [];
29
+ args.push(`[${new Date().toString()}]`);
30
+ for (var i = 0, count = arguments.length; i < count; i++) {
31
+ args.push(arguments[i]);
32
+ }
33
+ console.log.apply(console, args);
34
+ }
35
+
36
+
37
+ // 处理ev录屏消息
38
+ function handleEvRecord(conn, message) {
39
+ evConfig.conn = conn;
40
+
41
+ if (message.action === 'start') {
42
+ startEvRecording(conn);
43
+ } else if (message.action === 'stop') {
44
+ stopEvRecording(conn);
45
+ }
46
+ }
47
+
48
+ // 初始化函数
49
+ function init() {
50
+ // 直接启动runEv服务时,什么都不用做
51
+ // 等待前端连接和start消息,如果等到的是连接消息,记录连接时间。
52
+ log('evsockettool 初始化完成,等待前端连接');
53
+ }
54
+
55
+ // 启动ev录屏
56
+ function startEvRecording(conn) {
57
+ if (evConfig.recording) {
58
+ sendEvMessage(conn, 'error', '录屏已在进行中');
59
+ return;
60
+ }
61
+
62
+ evConfig.recording = true;
63
+ evConfig.startTime = new Date().getTime();
64
+ evConfig.connectTime = new Date().getTime();
65
+
66
+ log('收到前端录屏请求,启动时间:', new Date(evConfig.startTime).toString());
67
+
68
+ // 自动检测ev录屏工具路径
69
+ var evPath = autoDetectEvPath();
70
+ if (!evPath) {
71
+ watchEvOutput(conn);
72
+ sendEvMessage(conn, 'started', '未找到ev录屏工具,请手动启动');
73
+ return;
74
+ }
75
+
76
+ log('找到ev录屏工具:', evPath);
77
+
78
+ // 无论启动成功还是失败,都先开始监听存储目录的变动
79
+ watchEvOutput(conn);
80
+
81
+ // 启动ev录屏工具
82
+ try {
83
+ // 使用spawn替代exec,避免编码问题
84
+ var child = childProcess.spawn(evPath, [], {
85
+ windowsHide: true,
86
+ detached: true
87
+ });
88
+
89
+ var startupError = false;
90
+
91
+ child.on('error', function(error) {
92
+ startupError = true;
93
+ log('启动ev录屏工具失败:', error.message);
94
+ sendEvMessage(conn, 'started', '请手动启动,启动ev录屏工具失败: ' + error.message);
95
+ });
96
+
97
+ // 对于GUI应用程序,进程会在后台运行,不会立即退出
98
+ // 所以我们直接认为启动成功
99
+ setTimeout(function() {
100
+ if (!startupError) {
101
+ log('ev录屏工具已启动');
102
+ sendEvMessage(conn, 'started', 'ev录屏工具已启动,请开始录制');
103
+ }
104
+ }, 1000);
105
+
106
+ // 分离子进程,让它在后台运行
107
+ child.unref();
108
+ } catch (ex) {
109
+ log('启动ev录屏工具异常:', ex.message);
110
+ sendEvMessage(conn, 'started', '请手动启动,启动ev录屏工具异常: ' + ex.message);
111
+ }
112
+ }
113
+
114
+ // 停止ev录屏
115
+ function stopEvRecording(conn) {
116
+ log('收到停止录屏请求');
117
+ evConfig.recording = false;
118
+ sendEvMessage(conn, 'stopped', '已停止对ev的录屏监听');
119
+
120
+ // 清理监控文件
121
+ for (var filePath in monitoringFiles) {
122
+ if (monitoringFiles.hasOwnProperty(filePath)) {
123
+ clearInterval(monitoringFiles[filePath]);
124
+ delete monitoringFiles[filePath];
125
+ }
126
+ }
127
+
128
+ // 清理watcher
129
+ if (evConfig.watcher) {
130
+ evConfig.watcher.close();
131
+ evConfig.watcher = null;
132
+ log('清理文件监听器');
133
+ }
134
+
135
+ log('录屏服务已停止');
136
+ }
137
+
138
+ // 监听ev输出目录
139
+ function watchEvOutput(conn) {
140
+ if (evConfig.watcher) {
141
+ evConfig.watcher.close();
142
+ }
143
+
144
+ log('开始监控输出目录:', evConfig.outputDir);
145
+
146
+ evConfig.watcher = chokidar.watch(evConfig.outputDir, {
147
+ persistent: true,
148
+ usePolling: true,
149
+ ignoreInitial: true,
150
+ interval: 1000
151
+ });
152
+
153
+ evConfig.watcher.on('ready', function() {
154
+ log('文件监听器已就绪');
155
+ });
156
+
157
+ evConfig.watcher.on('add', function(filePath) {
158
+ var ext = path.extname(filePath).toLowerCase();
159
+ if (ext === '.mp3' || ext === '.wav' || ext === '.mp4') {
160
+ log('发现新的媒体文件:', filePath);
161
+
162
+ // 检查文件创建时间是否大于启动时间
163
+ try {
164
+ var stats = fs.statSync(filePath);
165
+ var fileCreateTime = stats.ctime.getTime();
166
+ if (fileCreateTime > evConfig.startTime) {
167
+ log('录屏开始,文件已生成:', filePath);
168
+ sendEvMessage(conn, 'recording', '录屏已开始,正在录制中');
169
+ }
170
+ } catch (ex) {
171
+ log('检查文件信息失败:', ex.message);
172
+ }
173
+ }
174
+ });
175
+
176
+ evConfig.watcher.on('change', function(filePath) {
177
+ var ext = path.extname(filePath).toLowerCase();
178
+ if (ext === '.mp3' || ext === '.wav' || ext === '.mp4') {
179
+ log('文件变化:', filePath);
180
+
181
+ // 检查文件创建时间和大小
182
+ try {
183
+ var stats = fs.statSync(filePath);
184
+ var fileCreateTime = stats.ctime.getTime();
185
+
186
+ // 文件创建时间必须大于启动时间
187
+ if (fileCreateTime > evConfig.startTime) {
188
+ log('文件创建时间符合要求:', new Date(fileCreateTime).toISOString());
189
+
190
+ // 如果文件还没有被监控,开始监控
191
+ if (!monitoringFiles[filePath]) {
192
+ log('开始监控文件:', filePath, '当前大小:', stats.size, 'bytes');
193
+ startMonitoringFile(conn, filePath);
194
+ }
195
+ } else {
196
+ log('文件创建时间不符合要求,跳过:', filePath);
197
+ }
198
+ } catch (ex) {
199
+ log('检查文件信息失败:', ex.message);
200
+ }
201
+ }
202
+ });
203
+
204
+ evConfig.watcher.on('error', function(error) {
205
+ log('文件监听器错误:', error.message);
206
+ });
207
+ }
208
+
209
+ // 监控文件大小变化,判断录制是否真正结束
210
+ function startMonitoringFile(conn, filePath) {
211
+ if (monitoringFiles[filePath]) {
212
+ return;
213
+ }
214
+
215
+ var lastSize = 0;
216
+ var stableCount = 0;
217
+ var hasGrowth = false;
218
+
219
+ // 根据文件类型设置不同的阈值
220
+ var ext = path.extname(filePath).toLowerCase();
221
+ var isVideo = ext === '.mp4';
222
+ var isAudio = ext === '.mp3' || ext === '.wav';
223
+
224
+ // MP4视频:帧率20fps,文件增长快,检查间隔1秒,稳定3次(3秒)即可判定完成
225
+ // MP3音频:码率128kbps,文件增长慢,检查间隔3秒,稳定2次(6秒)即可判定完成
226
+ var checkInterval = isVideo ? 1000 : (isAudio ? 3000 : 2000); // MP4: 1秒, MP3: 3秒, 其他: 2秒
227
+ var stableThreshold = isVideo ? 3 : (isAudio ? 2 : 3); // MP4: 3次(3秒), MP3: 2次(6秒), 其他: 3次
228
+ var minFileSize = isVideo ? 5000 : (isAudio ? 3000 : 1000); // MP4: 5KB, MP3: 3KB, 其他: 1KB
229
+
230
+ log('开始监控文件:', path.basename(filePath), '类型:', isVideo ? '视频' : (isAudio ? '音频' : '其他'),
231
+ '检查间隔:', checkInterval + 'ms', '稳定阈值:', stableThreshold + '次');
232
+
233
+ monitoringFiles[filePath] = setInterval(function() {
234
+ try {
235
+ var stats = fs.statSync(filePath);
236
+ var currentSize = stats.size;
237
+
238
+ if (currentSize > lastSize) {
239
+ hasGrowth = true;
240
+ stableCount = 0; // 重置计数
241
+ log('文件大小增长:', filePath, '当前大小:', currentSize, 'bytes');
242
+ } else if (currentSize === lastSize) {
243
+ stableCount++;
244
+ log('文件大小稳定:', filePath, '连续稳定次数:', stableCount, '稳定时间:', (stableCount * checkInterval / 1000).toFixed(1), '秒');
245
+ }
246
+
247
+ lastSize = currentSize;
248
+
249
+ // 当文件大小连续稳定5次(15秒)且至少有过一次增长,并且文件大小大于最小阈值时,认为录制真正结束
250
+ if (stableCount >= stableThreshold && hasGrowth && currentSize >= minFileSize) {
251
+ clearInterval(monitoringFiles[filePath]);
252
+ delete monitoringFiles[filePath];
253
+ log('文件录制真正完成:', filePath, '最终大小:', currentSize, 'bytes');
254
+ processEvFile(conn, filePath);
255
+ }
256
+ } catch (ex) {
257
+ log('监控文件失败:', ex.message);
258
+ clearInterval(monitoringFiles[filePath]);
259
+ delete monitoringFiles[filePath];
260
+ }
261
+ }, checkInterval);
262
+ }
263
+
264
+ // 处理ev生成的文件
265
+ function processEvFile(conn, filePath) {
266
+ // 模拟上传到七牛服务器的逻辑
267
+ var fileName = path.basename(filePath);
268
+ var fileExt = path.extname(fileName);
269
+ var timestamp = new Date().getTime();
270
+ var qiniuFileName = 'ev-recording/' + timestamp + fileExt;
271
+
272
+ // 模拟七牛服务器返回的URL
273
+ var audioUrl = 'https://cdn.example.com/' + qiniuFileName;
274
+
275
+ // 这里应该实现真正的七牛上传逻辑
276
+ // 例如使用qiniu SDK上传文件
277
+ // const qiniu = require('qiniu');
278
+ // const formUploader = new qiniu.form_up.FormUploader();
279
+ // const putExtra = new qiniu.form_up.PutExtra();
280
+ // const bucket = 'your-bucket';
281
+ // const key = qiniuFileName;
282
+ // formUploader.putFile(uploadToken, key, filePath, putExtra, function(respErr, respBody, respInfo) {
283
+ // if (respErr) {
284
+ // sendEvMessage(conn, 'error', '上传失败: ' + respErr.message);
285
+ // } else {
286
+ // if (respInfo.statusCode == 200) {
287
+ // var audioUrl = 'https://cdn.example.com/' + respBody.key;
288
+ // sendEvMessage(conn, 'completed', '录制完成', { audioUrl: audioUrl });
289
+ // } else {
290
+ // sendEvMessage(conn, 'error', '上传失败: ' + respInfo.statusCode);
291
+ // }
292
+ // }
293
+ // });
294
+
295
+ // 模拟上传成功
296
+ log('模拟上传文件到七牛服务器:', filePath, '->', audioUrl);
297
+
298
+ // 发送音频URL给前端
299
+ sendEvMessage(conn, 'completed', '录制完成', { audioUrl: audioUrl });
300
+
301
+ // 保持监控状态,等待下一次录制
302
+ }
303
+
304
+ // 发送ev消息给前端
305
+ function sendEvMessage(conn, status, message, data) {
306
+ var response = {
307
+ mode: 'evRecord',
308
+ status: status,
309
+ message: message,
310
+ timestamp: new Date().getTime()
311
+ };
312
+
313
+ if (data) {
314
+ Object.assign(response, data);
315
+ }
316
+
317
+ if (conn && conn.readyState === 1) {
318
+ conn.sendText(JSON.stringify(response));
319
+ }
320
+ }
321
+
322
+ // 自动检测ev录屏工具路径
323
+ function autoDetectEvPath() {
324
+ var possiblePaths = [
325
+ 'C:\\Program Files\\EVCapture\\EVCapture.exe',
326
+ 'C:\\Program Files (x86)\\EVCapture\\EVCapture.exe',
327
+ 'D:\\Program Files\\EVCapture\\EVCapture.exe',
328
+ 'D:\\Program Files (x86)\\EVCapture\\EVCapture.exe',
329
+ path.join(process.env.LocalAppData, 'EVCapture', 'EVCapture.exe'),
330
+ path.join(process.env.AppData, 'EVCapture', 'EVCapture.exe'),
331
+ path.join(process.env.USERPROFILE, 'Desktop', 'EVCapture.exe'),
332
+ process.env.EV_PATH
333
+ ];
334
+
335
+ for (var i = 0; i < possiblePaths.length; i++) {
336
+ var evPath = possiblePaths[i];
337
+ if (evPath && fs.existsSync(evPath)) {
338
+ return evPath;
339
+ }
340
+ }
341
+
342
+ return null;
343
+ }
344
+
345
+ // 导出处理函数
346
+ module.exports = {
347
+ handleEvRecord: handleEvRecord
348
+ };
349
+
350
+ // 初始化
351
+ init();
@@ -6,6 +6,17 @@ var chokidar = require('chokidar');
6
6
  var fs = require('fs');
7
7
  var exts = [".js", ".css", ".html", ".gspx"];
8
8
 
9
+ var evSocketTool = null;
10
+ if (config.evSavePath) {
11
+ // 引入ev socket工具
12
+ try {
13
+ evSocketTool = require('./evsockettool.js');
14
+ console.log('ev socket工具加载成功');
15
+ } catch (ex) {
16
+ utils.debug('ev socket工具加载失败:', ex);
17
+ }
18
+ }
19
+
9
20
  function watchWebChange() {
10
21
  utils.debug("创建文件变动监听");
11
22
 
@@ -34,7 +45,15 @@ function createSocket() {
34
45
  socketServer = ws.createServer(function(conn) {
35
46
  conn.on("text", function(str) {
36
47
  utils.debug("收到的信息为:" + str);
48
+
49
+ try {// 处理ev录屏消息
50
+ var message = JSON.parse(str);
51
+ if (message.mode === 'evRecord' && evSocketTool) {
52
+ evSocketTool.handleEvRecord(conn, message);
53
+ }
54
+ } catch (ex) { }
37
55
  });
56
+
38
57
  conn.on('connect', function(code, reason) {
39
58
  utils.log('connect连接成功');
40
59
  broadcast('ws connect success');
@@ -61,7 +80,10 @@ function broadcast(msg) {
61
80
 
62
81
  var socketServer;
63
82
  createSocket();
64
- watchWebChange();
83
+
84
+ if (config.webPath) {
85
+ watchWebChange();
86
+ }
65
87
 
66
88
  // 暴露broadcast函数给全局使用,供openFileWatcher等模块调用
67
89
  global.socketBroadcast = broadcast;
package/tool/start.js CHANGED
@@ -370,6 +370,30 @@ class Start {
370
370
  upgrade.createProject(options);
371
371
  }
372
372
 
373
+ // 启动ev录屏服务
374
+ runEvServer() {
375
+ // 设置默认的ev音视频保存路径为当前用户路径
376
+ if (!config.evSavePath) {
377
+ config.evSavePath = process.env.UserProfile;
378
+ console.log('使用默认ev保存路径:', config.evSavePath);
379
+ }
380
+
381
+ if (!fs.existsSync(config.evSavePath)) {
382
+ throw new Error(`evSavePath参数错误,请参考文档。配置的evSavePath路径不存在,支持相对路径,路径中用\双斜杠或单反斜杠/;请检查拼写是否正确:${config.evSavePath}`);
383
+ }
384
+
385
+ if (!config.port) {
386
+ config.port = 63096; // 默认端口
387
+ }
388
+
389
+ console.log('启动ev录屏服务...');
390
+ console.log('ev音视频保存路径:', config.evSavePath);
391
+ console.log('服务端口:', config.port);
392
+
393
+ // 启动服务器
394
+ require('../lib/server.js').startEvServer();
395
+ }
396
+
373
397
  bindBranchName(webPath) {
374
398
  if (config.branchName) {
375
399
  return true;