mb-rrvideo-server 1.0.4 → 1.0.6
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.
- package/DOCKER.md +14 -2
- package/Dockerfile +8 -3
- package/bin/rrvideo-server +5 -4
- package/config.example.json +10 -4
- package/ecosystem.docker.config.js +2 -1
- package/package.json +2 -1
- package/src/config.js +10 -2
- package/src/index.js +37 -31
- package/src/storage.js +60 -1
package/DOCKER.md
CHANGED
|
@@ -16,8 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
**快速参考:**
|
|
18
18
|
```bash
|
|
19
|
-
|
|
20
|
-
npm pack mb-rrvideo-server
|
|
19
|
+
|
|
21
20
|
|
|
22
21
|
# 构建 Docker 镜像
|
|
23
22
|
docker build -t mb-rrvideo-converter:latest .
|
|
@@ -31,8 +30,14 @@ docker-compose up -d
|
|
|
31
30
|
#第一次安装
|
|
32
31
|
sudo docker compose up -d --build --progress=plain
|
|
33
32
|
|
|
33
|
+
# 更新docker镜像:
|
|
34
|
+
sudo docker compose build --no-cache
|
|
35
|
+
|
|
34
36
|
# 容器名称
|
|
35
37
|
docker ps | grep rrvideo-server
|
|
38
|
+
|
|
39
|
+
# 下载 npm 包
|
|
40
|
+
npm pack mb-rrvideo-server
|
|
36
41
|
```
|
|
37
42
|
|
|
38
43
|
## 目录
|
|
@@ -266,6 +271,13 @@ docker-compose up -d
|
|
|
266
271
|
docker-compose ps
|
|
267
272
|
```
|
|
268
273
|
|
|
274
|
+
更新后重装:
|
|
275
|
+
# 强制不使用缓存构建,确保拉到最新的 npm 包
|
|
276
|
+
sudo docker compose build --no-cache
|
|
277
|
+
|
|
278
|
+
# 重启服务
|
|
279
|
+
sudo docker compose up -d
|
|
280
|
+
|
|
269
281
|
**输出示例:**
|
|
270
282
|
```
|
|
271
283
|
NAME IMAGE STATUS PORTS
|
package/Dockerfile
CHANGED
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
FROM node:22-slim
|
|
8
8
|
|
|
9
|
+
# 设置代理参数(可从构建参数传入,也可在此处直接定义默认值)
|
|
10
|
+
# 如果不想每次构建都传参,可以在这里写死默认值
|
|
11
|
+
ARG HTTP_PROXY="http://172.168.1.117:10809"
|
|
12
|
+
ARG HTTPS_PROXY="http://172.168.1.117:10809"
|
|
13
|
+
|
|
9
14
|
# 设置环境变量
|
|
10
15
|
ENV TZ=Asia/Shanghai \
|
|
11
16
|
NODE_ENV=production \
|
|
@@ -70,10 +75,10 @@ RUN npm install -g --verbose --ignore-scripts \
|
|
|
70
75
|
|
|
71
76
|
# ============================================
|
|
72
77
|
# 第六步:安装 Playwright 浏览器(Chromium)
|
|
73
|
-
#
|
|
78
|
+
# 使用构建参数中的代理(只在此步骤生效)
|
|
74
79
|
# ============================================
|
|
75
|
-
RUN export
|
|
76
|
-
export
|
|
80
|
+
RUN export HTTP_PROXY=$HTTP_PROXY && \
|
|
81
|
+
export HTTPS_PROXY=$HTTPS_PROXY && \
|
|
77
82
|
npx playwright install chromium
|
|
78
83
|
|
|
79
84
|
# ============================================
|
package/bin/rrvideo-server
CHANGED
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
const path = require('path');
|
|
8
8
|
|
|
9
|
-
//
|
|
10
|
-
const projectRoot = path.resolve(__dirname, '..');
|
|
11
|
-
process.chdir(projectRoot);
|
|
9
|
+
// 设置工作目录为项目根目录(如果有必要,或者保持当前目录)
|
|
10
|
+
// const projectRoot = path.resolve(__dirname, '..');
|
|
11
|
+
// process.chdir(projectRoot);
|
|
12
12
|
|
|
13
13
|
// 启动服务
|
|
14
|
-
require('../src/index.js');
|
|
14
|
+
const { startServer } = require('../src/index.js');
|
|
15
|
+
startServer();
|
package/config.example.json
CHANGED
|
@@ -7,23 +7,29 @@
|
|
|
7
7
|
|
|
8
8
|
// 存储配置
|
|
9
9
|
"storage": {
|
|
10
|
-
"type": "
|
|
10
|
+
"type": "aliyun", // 存储类型: "local" 或 "minio"
|
|
11
11
|
|
|
12
12
|
// 本地存储配置
|
|
13
13
|
"local": {
|
|
14
14
|
"video_dir": "./Video", // 视频存储目录
|
|
15
15
|
"temp_dir": "./temp" // 临时文件目录
|
|
16
16
|
},
|
|
17
|
+
"aliyun":{
|
|
18
|
+
"accessKeyId": "",
|
|
19
|
+
"accessKeySecret": "",
|
|
20
|
+
"endpoint": "",
|
|
21
|
+
"bucket": ""
|
|
22
|
+
},
|
|
17
23
|
|
|
18
24
|
// MinIO 对象存储配置
|
|
19
25
|
"minio": {
|
|
20
|
-
"endpoint": "
|
|
26
|
+
"endpoint": "",
|
|
21
27
|
"port": 9000,
|
|
22
28
|
"useSSL": false,
|
|
23
29
|
"accessKey": "your-access-key",
|
|
24
30
|
"secretKey": "your-secret-key",
|
|
25
|
-
"bucket": "
|
|
26
|
-
"
|
|
31
|
+
"bucket": "",
|
|
32
|
+
"publicHost": ""
|
|
27
33
|
}
|
|
28
34
|
},
|
|
29
35
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mb-rrvideo-server",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.6",
|
|
4
4
|
"description": "视频转码服务 - 接收可回溯机请求,执行转码/合并,上传MinIO/本地存储",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -35,6 +35,7 @@
|
|
|
35
35
|
"author": "",
|
|
36
36
|
"license": "MIT",
|
|
37
37
|
"dependencies": {
|
|
38
|
+
"ali-oss": "^6.23.0",
|
|
38
39
|
"minio": "^7.1.3"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
package/src/config.js
CHANGED
|
@@ -138,8 +138,8 @@ class ConfigManager {
|
|
|
138
138
|
}
|
|
139
139
|
|
|
140
140
|
const storageType = this.config.storage.type;
|
|
141
|
-
if (storageType !== 'local' && storageType !== 'minio') {
|
|
142
|
-
throw new Error('storage.type 必须是 local 或
|
|
141
|
+
if (storageType !== 'local' && storageType !== 'minio' && storageType !== 'aliyun') {
|
|
142
|
+
throw new Error('storage.type 必须是 local, minio 或 aliyun');
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
// 如果使用 MinIO,验证 MinIO 配置
|
|
@@ -150,6 +150,14 @@ class ConfigManager {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
+
// 如果使用 Aliyun OSS,验证 Aliyun OSS 配置
|
|
154
|
+
if (storageType === 'aliyun') {
|
|
155
|
+
const aliyun = this.config.storage.aliyun;
|
|
156
|
+
if (!aliyun || !aliyun.accessKeyId || !aliyun.accessKeySecret || !aliyun.endpoint || !aliyun.bucket) {
|
|
157
|
+
throw new Error('阿里云 OSS 配置不完整');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
153
161
|
console.log('[Config] 配置验证通过');
|
|
154
162
|
console.log('[Config] 存储类型:', storageType);
|
|
155
163
|
}
|
package/src/index.js
CHANGED
|
@@ -11,6 +11,35 @@ const { handleReceiveRequest } = require('./routes/receive');
|
|
|
11
11
|
const { handleConvertV1, handleConvertV2 } = require('./routes/convert');
|
|
12
12
|
const { handleMerge } = require('./routes/merge');
|
|
13
13
|
|
|
14
|
+
// 全局错误处理
|
|
15
|
+
process.on('exit', (code) => {
|
|
16
|
+
console.log(`进程退出,代码: ${code}`);
|
|
17
|
+
logger.flushAll();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
process.on('SIGINT', () => {
|
|
21
|
+
console.log('收到 SIGINT 信号,正在关闭...');
|
|
22
|
+
logger.flushAll();
|
|
23
|
+
process.exit(0);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
process.on('SIGTERM', () => {
|
|
27
|
+
console.log('收到 SIGTERM 信号,正在关闭...');
|
|
28
|
+
logger.flushAll();
|
|
29
|
+
process.exit(0);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
process.on('uncaughtException', (err) => {
|
|
33
|
+
console.error(`未捕获的异常: ${err.message}`);
|
|
34
|
+
console.error(err.stack);
|
|
35
|
+
logger.flushAll();
|
|
36
|
+
process.exit(1);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
process.on('unhandledRejection', (reason, promise) => {
|
|
40
|
+
console.error(`未处理的 Promise 拒绝: ${reason}`);
|
|
41
|
+
});
|
|
42
|
+
|
|
14
43
|
// 创建 HTTP 服务器
|
|
15
44
|
const server = http.createServer((req, res) => {
|
|
16
45
|
try {
|
|
@@ -94,8 +123,8 @@ const server = http.createServer((req, res) => {
|
|
|
94
123
|
}
|
|
95
124
|
});
|
|
96
125
|
|
|
97
|
-
//
|
|
98
|
-
|
|
126
|
+
// 封装启动逻辑
|
|
127
|
+
function startServer() {
|
|
99
128
|
const PORT = config.get('server.port', 24203);
|
|
100
129
|
const HOST = config.get('server.host', '0.0.0.0');
|
|
101
130
|
|
|
@@ -104,35 +133,12 @@ if (require.main === module) {
|
|
|
104
133
|
console.log(`Storage type: ${config.get('storage.type')}`);
|
|
105
134
|
console.log(`Log level: ${config.get('log.level')}`);
|
|
106
135
|
});
|
|
107
|
-
|
|
108
|
-
// 程序异常或提前终止时的处理
|
|
109
|
-
process.on('exit', (code) => {
|
|
110
|
-
console.log(`进程退出,代码: ${code}`);
|
|
111
|
-
logger.flushAll();
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
process.on('SIGINT', () => {
|
|
115
|
-
console.log('收到 SIGINT 信号,正在关闭...');
|
|
116
|
-
logger.flushAll();
|
|
117
|
-
process.exit(0);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
process.on('SIGTERM', () => {
|
|
121
|
-
console.log('收到 SIGTERM 信号,正在关闭...');
|
|
122
|
-
logger.flushAll();
|
|
123
|
-
process.exit(0);
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
process.on('uncaughtException', (err) => {
|
|
127
|
-
console.error(`未捕获的异常: ${err.message}`);
|
|
128
|
-
console.error(err.stack);
|
|
129
|
-
logger.flushAll();
|
|
130
|
-
process.exit(1);
|
|
131
|
-
});
|
|
136
|
+
}
|
|
132
137
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
// 如果是直接运行脚本,则启动服务器
|
|
139
|
+
if (require.main === module) {
|
|
140
|
+
startServer();
|
|
136
141
|
}
|
|
137
142
|
|
|
138
|
-
|
|
143
|
+
// 导出 server 和启动函数
|
|
144
|
+
module.exports = { server, startServer };
|
package/src/storage.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const Minio = require('minio');
|
|
4
|
+
const OSS = require('ali-oss');
|
|
4
5
|
const config = require('./config');
|
|
5
6
|
|
|
6
7
|
class StorageManager {
|
|
7
8
|
constructor() {
|
|
8
9
|
this.storageType = null;
|
|
9
10
|
this.minioClient = null;
|
|
11
|
+
this.ossClient = null;
|
|
10
12
|
this.init();
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -24,6 +26,17 @@ class StorageManager {
|
|
|
24
26
|
secretKey: minioConfig.secretKey
|
|
25
27
|
});
|
|
26
28
|
console.log('[Storage] MinIO 客户端已初始化');
|
|
29
|
+
} else if (this.storageType === 'aliyun') {
|
|
30
|
+
const aliyunConfig = storageConfig.aliyun;
|
|
31
|
+
this.ossClient = new OSS({
|
|
32
|
+
// region: aliyunConfig.region || 'oss-cn-hangzhou', // 默认为杭州,或者从endpoint解析
|
|
33
|
+
accessKeyId: aliyunConfig.accessKeyId,
|
|
34
|
+
accessKeySecret: aliyunConfig.accessKeySecret,
|
|
35
|
+
bucket: aliyunConfig.bucket,
|
|
36
|
+
endpoint: aliyunConfig.endpoint,
|
|
37
|
+
secure: true // 默认使用 HTTPS
|
|
38
|
+
});
|
|
39
|
+
console.log('[Storage] 阿里云 OSS 客户端已初始化');
|
|
27
40
|
} else {
|
|
28
41
|
console.log('[Storage] 使用本地存储');
|
|
29
42
|
}
|
|
@@ -64,6 +77,8 @@ class StorageManager {
|
|
|
64
77
|
|
|
65
78
|
if (this.storageType === 'minio') {
|
|
66
79
|
return await this.uploadToMinio(localPath, remotePath);
|
|
80
|
+
} else if (this.storageType === 'aliyun') {
|
|
81
|
+
return await this.uploadToOss(localPath, remotePath);
|
|
67
82
|
} else {
|
|
68
83
|
return await this.saveToLocal(localPath, remotePath);
|
|
69
84
|
}
|
|
@@ -95,7 +110,7 @@ class StorageManager {
|
|
|
95
110
|
await this.minioClient.fPutObject(bucket, remotePath, localPath, metaData);
|
|
96
111
|
|
|
97
112
|
// 生成公网访问 URL
|
|
98
|
-
const publicHost = minioConfig.
|
|
113
|
+
const publicHost = minioConfig.publicHost;
|
|
99
114
|
const videoUrl = `${publicHost}/${bucket}/${remotePath}`;
|
|
100
115
|
|
|
101
116
|
console.log('[Storage] MinIO 上传成功:', videoUrl);
|
|
@@ -106,6 +121,50 @@ class StorageManager {
|
|
|
106
121
|
}
|
|
107
122
|
}
|
|
108
123
|
|
|
124
|
+
/**
|
|
125
|
+
* 上传到 阿里云 OSS
|
|
126
|
+
*/
|
|
127
|
+
async uploadToOss(localPath, remotePath) {
|
|
128
|
+
try {
|
|
129
|
+
// 检查文件是否存在
|
|
130
|
+
if (!fs.existsSync(localPath)) {
|
|
131
|
+
throw new Error(`本地文件不存在: ${localPath}`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// 上传选项
|
|
135
|
+
const headers = {
|
|
136
|
+
// 指定Object的存储类型。
|
|
137
|
+
'x-oss-storage-class': 'Standard',
|
|
138
|
+
// 指定Object的访问权限。
|
|
139
|
+
'x-oss-object-acl': 'public-read', // 通常视频需要公共读权限以便播放
|
|
140
|
+
// 指定PutObject操作时是否覆盖同名目标Object。此处设置为true,表示禁止覆盖同名Object。
|
|
141
|
+
'x-oss-forbid-overwrite': 'false', // 允许覆盖
|
|
142
|
+
'Content-Type': 'video/mp4'
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
const result = await this.ossClient.put(remotePath, path.normalize(localPath), { headers });
|
|
146
|
+
|
|
147
|
+
// 生成访问 URL
|
|
148
|
+
// 优先使用 result.url,如果配置了自定义域名(publicHost)则使用自定义域名
|
|
149
|
+
const aliyunConfig = config.get('storage.aliyun');
|
|
150
|
+
let videoUrl = result.url;
|
|
151
|
+
|
|
152
|
+
if (aliyunConfig.publicHost) {
|
|
153
|
+
// 确保 publicHost 不带末尾斜杠
|
|
154
|
+
const host = aliyunConfig.publicHost.replace(/\/$/, '');
|
|
155
|
+
// 确保 remotePath 不带开头斜杠
|
|
156
|
+
const key = remotePath.replace(/^\//, '');
|
|
157
|
+
videoUrl = `${host}/${key}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
console.log('[Storage] 阿里云 OSS 上传成功:', videoUrl);
|
|
161
|
+
return videoUrl;
|
|
162
|
+
} catch (error) {
|
|
163
|
+
console.error('[Storage] 阿里云 OSS 上传失败:', error.message);
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
109
168
|
/**
|
|
110
169
|
* 保存到本地
|
|
111
170
|
*/
|