mb-rrvideo-server 1.0.3 → 1.0.5
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 +15 -4
- package/Dockerfile +29 -36
- package/config.example.json +10 -4
- package/ecosystem.docker.config.js +2 -1
- package/package.json +2 -1
- package/src/config.js +26 -3
- package/src/storage.js +60 -1
package/DOCKER.md
CHANGED
|
@@ -28,6 +28,9 @@ docker images mb-rrvideo-converter
|
|
|
28
28
|
# 运行容器
|
|
29
29
|
docker-compose up -d
|
|
30
30
|
|
|
31
|
+
#第一次安装
|
|
32
|
+
sudo docker compose up -d --build --progress=plain
|
|
33
|
+
|
|
31
34
|
# 容器名称
|
|
32
35
|
docker ps | grep rrvideo-server
|
|
33
36
|
```
|
|
@@ -98,7 +101,7 @@ docker --version
|
|
|
98
101
|
|
|
99
102
|
```bash
|
|
100
103
|
# 下载 Docker Compose
|
|
101
|
-
sudo curl -L "https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
104
|
+
sudo curl -L "https://gh-proxy.org/https://github.com/docker/compose/releases/download/v2.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
|
|
102
105
|
|
|
103
106
|
# 添加执行权限
|
|
104
107
|
sudo chmod +x /usr/local/bin/docker-compose
|
|
@@ -117,9 +120,10 @@ sudo mkdir -p /etc/docker
|
|
|
117
120
|
sudo tee /etc/docker/daemon.json <<-'EOF'
|
|
118
121
|
{
|
|
119
122
|
"registry-mirrors": [
|
|
120
|
-
"https://docker.
|
|
121
|
-
"https://
|
|
122
|
-
"https://mirror.
|
|
123
|
+
"https://docker.m.daocloud.io",
|
|
124
|
+
"https://dockerproxy.com",
|
|
125
|
+
"https://mirror.baidubce.com",
|
|
126
|
+
"https://docker.nju.edu.cn"
|
|
123
127
|
]
|
|
124
128
|
}
|
|
125
129
|
EOF
|
|
@@ -262,6 +266,13 @@ docker-compose up -d
|
|
|
262
266
|
docker-compose ps
|
|
263
267
|
```
|
|
264
268
|
|
|
269
|
+
更新后重装:
|
|
270
|
+
# 强制不使用缓存构建,确保拉到最新的 npm 包
|
|
271
|
+
sudo docker compose build --no-cache
|
|
272
|
+
|
|
273
|
+
# 重启服务
|
|
274
|
+
sudo docker compose up -d
|
|
275
|
+
|
|
265
276
|
**输出示例:**
|
|
266
277
|
```
|
|
267
278
|
NAME IMAGE STATUS PORTS
|
package/Dockerfile
CHANGED
|
@@ -6,11 +6,18 @@
|
|
|
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 \
|
|
12
|
-
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=
|
|
13
|
-
PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright
|
|
17
|
+
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
|
|
18
|
+
PLAYWRIGHT_BROWSERS_PATH=/root/.cache/ms-playwright
|
|
19
|
+
|
|
20
|
+
# PLAYWRIGHT_DOWNLOAD_HOST=https://npmmirror.com/mirrors/playwright
|
|
14
21
|
|
|
15
22
|
# ============================================
|
|
16
23
|
# 第一步:配置国内镜像源(加速构建)
|
|
@@ -56,42 +63,26 @@ RUN apt-get update && apt-get install -y \
|
|
|
56
63
|
RUN npm config set registry https://registry.npmmirror.com
|
|
57
64
|
|
|
58
65
|
# ============================================
|
|
59
|
-
#
|
|
60
|
-
#
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
# 第五步:全局安装 Node.js 包
|
|
67
|
+
# 使用 --verbose 查看详细日志
|
|
68
|
+
# 并显式添加 --ignore-scripts 参数以跳过所有 postinstall 脚本
|
|
63
69
|
# ============================================
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
RUN cd /tmp/mb-rrvideo && \
|
|
70
|
-
# 安装依赖(包含 devDependencies 以便执行 build)
|
|
71
|
-
npm install && \
|
|
72
|
-
# 编译 TypeScript
|
|
73
|
-
npm run build && \
|
|
74
|
-
# 全局安装
|
|
75
|
-
npm install -g . && \
|
|
76
|
-
# 清理源码和缓存
|
|
77
|
-
cd / && rm -rf /tmp/mb-rrvideo && npm cache clean --force
|
|
70
|
+
RUN npm install -g --verbose --ignore-scripts \
|
|
71
|
+
pm2 \
|
|
72
|
+
mb-rrvideo \
|
|
73
|
+
mb-rrvideo-server \
|
|
74
|
+
&& npm cache clean --force
|
|
78
75
|
|
|
79
76
|
# ============================================
|
|
80
|
-
#
|
|
77
|
+
# 第六步:安装 Playwright 浏览器(Chromium)
|
|
78
|
+
# 使用构建参数中的代理(只在此步骤生效)
|
|
81
79
|
# ============================================
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# 安装当前项目依赖并链接命令
|
|
86
|
-
RUN npm install -g . && npm cache clean --force
|
|
80
|
+
RUN export HTTP_PROXY=$HTTP_PROXY && \
|
|
81
|
+
export HTTPS_PROXY=$HTTPS_PROXY && \
|
|
82
|
+
npx playwright install chromium
|
|
87
83
|
|
|
88
84
|
# ============================================
|
|
89
|
-
#
|
|
90
|
-
# ============================================
|
|
91
|
-
RUN npx playwright install chromium
|
|
92
|
-
|
|
93
|
-
# ============================================
|
|
94
|
-
# 第八步:创建应用目录
|
|
85
|
+
# 第七步:创建应用目录
|
|
95
86
|
# ============================================
|
|
96
87
|
RUN mkdir -p /app/config \
|
|
97
88
|
&& mkdir -p /app/logs \
|
|
@@ -99,23 +90,25 @@ RUN mkdir -p /app/config \
|
|
|
99
90
|
&& mkdir -p /app/Video \
|
|
100
91
|
&& mkdir -p /app/tasks
|
|
101
92
|
|
|
93
|
+
WORKDIR /app
|
|
94
|
+
|
|
102
95
|
# ============================================
|
|
103
|
-
#
|
|
96
|
+
# 第八步:复制 PM2 配置文件
|
|
104
97
|
# ============================================
|
|
105
98
|
COPY ecosystem.docker.config.js /app/ecosystem.config.js
|
|
106
99
|
|
|
107
100
|
# ============================================
|
|
108
|
-
#
|
|
101
|
+
# 第九步:暴露端口
|
|
109
102
|
# ============================================
|
|
110
103
|
EXPOSE 24203
|
|
111
104
|
|
|
112
105
|
# ============================================
|
|
113
|
-
#
|
|
106
|
+
# 第十步:健康检查
|
|
114
107
|
# ============================================
|
|
115
108
|
HEALTHCHECK --interval=30s --timeout=10s --retries=3 \
|
|
116
109
|
CMD curl -f http://localhost:24203/health || exit 1
|
|
117
110
|
|
|
118
111
|
# ============================================
|
|
119
|
-
#
|
|
112
|
+
# 第十一步:启动服务
|
|
120
113
|
# ============================================
|
|
121
114
|
CMD ["pm2-runtime", "start", "ecosystem.config.js"]
|
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.5",
|
|
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
|
@@ -4,7 +4,22 @@ const path = require('path');
|
|
|
4
4
|
class ConfigManager {
|
|
5
5
|
constructor() {
|
|
6
6
|
this.config = null;
|
|
7
|
-
|
|
7
|
+
// 优先使用环境变量 CONFIG_PATH,如果没设置,再按顺序查找
|
|
8
|
+
// 1. 当前工作目录下的 config.json (本地开发路径)
|
|
9
|
+
// 2. 源码所在目录的上级目录 (兜底)
|
|
10
|
+
|
|
11
|
+
if (process.env.CONFIG_PATH) {
|
|
12
|
+
this.configPath = process.env.CONFIG_PATH;
|
|
13
|
+
} else {
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const candidates = [
|
|
16
|
+
path.join(process.cwd(), 'config.json'),
|
|
17
|
+
path.join(__dirname, '../config.json')
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
this.configPath = candidates.find(p => fs.existsSync(p)) || candidates[1];
|
|
21
|
+
}
|
|
22
|
+
|
|
8
23
|
this.examplePath = path.join(__dirname, '../config.example.json');
|
|
9
24
|
}
|
|
10
25
|
|
|
@@ -123,8 +138,8 @@ class ConfigManager {
|
|
|
123
138
|
}
|
|
124
139
|
|
|
125
140
|
const storageType = this.config.storage.type;
|
|
126
|
-
if (storageType !== 'local' && storageType !== 'minio') {
|
|
127
|
-
throw new Error('storage.type 必须是 local 或
|
|
141
|
+
if (storageType !== 'local' && storageType !== 'minio' && storageType !== 'aliyun') {
|
|
142
|
+
throw new Error('storage.type 必须是 local, minio 或 aliyun');
|
|
128
143
|
}
|
|
129
144
|
|
|
130
145
|
// 如果使用 MinIO,验证 MinIO 配置
|
|
@@ -135,6 +150,14 @@ class ConfigManager {
|
|
|
135
150
|
}
|
|
136
151
|
}
|
|
137
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
|
+
|
|
138
161
|
console.log('[Config] 配置验证通过');
|
|
139
162
|
console.log('[Config] 存储类型:', storageType);
|
|
140
163
|
}
|
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
|
*/
|