@xiping/node-utils 1.0.53 → 1.0.61
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/lib/src/ffmpeg/README.md +371 -0
- package/lib/src/ffmpeg/README_cutVideo.md +220 -0
- package/lib/src/ffmpeg/extractAudio.d.ts +36 -0
- package/lib/src/ffmpeg/extractAudio.js +186 -0
- package/lib/src/ffmpeg/getThumbnail.d.ts +15 -0
- package/lib/src/ffmpeg/getThumbnail.js +64 -10
- package/lib/src/ffmpeg/getVideoInfo.d.ts +47 -0
- package/lib/src/ffmpeg/getVideoInfo.js +109 -0
- package/lib/src/ffmpeg/index.d.ts +2 -47
- package/lib/src/ffmpeg/index.js +2 -109
- package/lib/src/image/README.md +230 -0
- package/lib/src/srt-to-vtt/README.md +189 -0
- package/package.json +3 -3
|
@@ -0,0 +1,371 @@
|
|
|
1
|
+
# FFmpeg 工具模块
|
|
2
|
+
|
|
3
|
+
基于 FFmpeg/FFprobe 的 Node.js 视频处理工具集,提供视频信息获取、缩略图生成、视频截取、音频提取及运行环境检查等功能。
|
|
4
|
+
|
|
5
|
+
本模块由 `@xiping/node-utils` 包统一导出,使用时从包根路径导入即可:`import { ... } from '@xiping/node-utils'`。
|
|
6
|
+
|
|
7
|
+
## 功能概览
|
|
8
|
+
|
|
9
|
+
| 功能 | 说明 | 依赖 |
|
|
10
|
+
|------------|--------------------------|-------------|
|
|
11
|
+
| 视频信息 | 时长、分辨率、编码、比特率等 | ffprobe |
|
|
12
|
+
| 缩略图生成 | 多帧合成预览图(AVIF/WebP/JPEG/PNG) | ffmpeg + sharp |
|
|
13
|
+
| 视频截取 | 按时间范围截取,流复制不重编码 | ffmpeg |
|
|
14
|
+
| 提取音频 | 从视频中提取音轨为 mp3/m4a/wav | ffmpeg |
|
|
15
|
+
| 环境检查 | 检测 ffmpeg/ffprobe 是否可用 | - |
|
|
16
|
+
|
|
17
|
+
## 安装要求
|
|
18
|
+
|
|
19
|
+
### 依赖包
|
|
20
|
+
|
|
21
|
+
- Node.js
|
|
22
|
+
- [shelljs](https://www.npmjs.com/package/shelljs)
|
|
23
|
+
- [sharp](https://www.npmjs.com/package/sharp)(仅缩略图功能需要)
|
|
24
|
+
|
|
25
|
+
### 系统要求
|
|
26
|
+
|
|
27
|
+
- **ffprobe**:视频信息相关接口需要(通常随 ffmpeg 一起安装)
|
|
28
|
+
- **ffmpeg**:缩略图、视频截取需要
|
|
29
|
+
|
|
30
|
+
### 安装 FFmpeg / FFprobe
|
|
31
|
+
|
|
32
|
+
**macOS:**
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
brew install ffmpeg
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Ubuntu/Debian:**
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
sudo apt update
|
|
42
|
+
sudo apt install ffmpeg
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**CentOS/RHEL:**
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
sudo yum install ffmpeg
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Windows:**
|
|
52
|
+
|
|
53
|
+
从 [FFmpeg 官网](https://ffmpeg.org/download.html) 下载并将可执行文件加入 PATH,或使用 Chocolatey:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
choco install ffmpeg
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 1. 视频信息(getVideoInfo)
|
|
62
|
+
|
|
63
|
+
使用 ffprobe 获取视频元数据:时长、分辨率、编码、比特率、帧率、文件大小等。
|
|
64
|
+
|
|
65
|
+
### 基本使用
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { getVideoInfo } from '@xiping/node-utils';
|
|
69
|
+
|
|
70
|
+
const videoInfo = getVideoInfo('/path/to/video.mp4');
|
|
71
|
+
console.log(videoInfo.durationFormatted, videoInfo.width, videoInfo.height);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 检查 ffprobe 是否可用
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { isFfprobeAvailable } from '@xiping/node-utils';
|
|
78
|
+
|
|
79
|
+
if (isFfprobeAvailable()) {
|
|
80
|
+
console.log('ffprobe 可用');
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 批量获取
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import { getMultipleVideoInfo } from '@xiping/node-utils';
|
|
88
|
+
|
|
89
|
+
const infos = getMultipleVideoInfo(['/path/video1.mp4', '/path/video2.mp4']);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### 获取详细元数据(含章节等)
|
|
93
|
+
|
|
94
|
+
```typescript
|
|
95
|
+
import { getDetailedVideoInfo } from '@xiping/node-utils';
|
|
96
|
+
|
|
97
|
+
const detailed = getDetailedVideoInfo('/path/to/video.mp4');
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### VideoInfo 结构
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
interface VideoInfo {
|
|
104
|
+
path: string;
|
|
105
|
+
duration: number; // 秒
|
|
106
|
+
durationFormatted: string;
|
|
107
|
+
width: number;
|
|
108
|
+
height: number;
|
|
109
|
+
videoCodec: string;
|
|
110
|
+
audioCodec: string;
|
|
111
|
+
bitrate: number;
|
|
112
|
+
fps: number;
|
|
113
|
+
fileSize: number; // 字节
|
|
114
|
+
fileSizeFormatted: string;
|
|
115
|
+
rawInfo: string; // 原始 ffprobe JSON
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## 2. 缩略图生成(getThumbnail)
|
|
122
|
+
|
|
123
|
+
从视频中等间隔提取多帧,用 sharp 合成为一张缩略图,支持 AVIF/WebP/JPEG/PNG。
|
|
124
|
+
|
|
125
|
+
### 基本使用
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
import { getThumbnail } from '@xiping/node-utils';
|
|
129
|
+
|
|
130
|
+
const result = await getThumbnail('/path/to/video.mp4', {
|
|
131
|
+
frames: 60, // 提取帧数(默认 60)
|
|
132
|
+
columns: 4, // 每行列数(默认 4)
|
|
133
|
+
outputWidth: 3840, // 输出宽度(默认 3840)
|
|
134
|
+
format: 'avif', // 'avif' | 'webp' | 'jpeg' | 'png'
|
|
135
|
+
quality: 80, // 1–100
|
|
136
|
+
outputFileName: 'thumbnail.avif',
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
console.log(result.outputPath);
|
|
140
|
+
console.log(result.metadata);
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### 进度回调
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const result = await getThumbnail('/path/to/video.mp4', {
|
|
147
|
+
frames: 30,
|
|
148
|
+
onProgress(progress) {
|
|
149
|
+
console.log(progress.phase, progress.percent, progress.message);
|
|
150
|
+
// phase: 'analyzing' | 'extracting' | 'composing' | 'encoding' | 'done'
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### 选项说明
|
|
156
|
+
|
|
157
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
158
|
+
|-----------------|----------|----------------|----------------|
|
|
159
|
+
| frames | number | 60 | 提取的帧数 |
|
|
160
|
+
| columns | number | 4 | 缩略图列数 |
|
|
161
|
+
| outputWidth | number | 3840 | 输出图宽度 |
|
|
162
|
+
| outputFileName | string | thumbnail.avif | 输出文件名 |
|
|
163
|
+
| format | string | 'avif' | avif/webp/jpeg/png |
|
|
164
|
+
| quality | number | 80 | 1–100 |
|
|
165
|
+
| batchSize | number | 10 | 合成时每批帧数 |
|
|
166
|
+
| maxConcurrency | number | 4 | 提取帧并发数 |
|
|
167
|
+
| tempDir | string | - | 自定义临时目录 |
|
|
168
|
+
| onProgress | function | - | 进度回调 |
|
|
169
|
+
|
|
170
|
+
### 返回值
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
{
|
|
174
|
+
buffer: Buffer;
|
|
175
|
+
outputPath: string;
|
|
176
|
+
metadata: {
|
|
177
|
+
frames: number;
|
|
178
|
+
duration: number;
|
|
179
|
+
outputSize: { width: number; height: number };
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**注意**:输入路径需为**绝对路径**;使用前请确保已安装 ffmpeg(可用下方「环境检查」接口检测)。
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## 3. 视频截取(cutVideo)
|
|
189
|
+
|
|
190
|
+
按开始时间、时长或结束时间截取片段,使用流复制(`-c copy`),不重新编码。
|
|
191
|
+
|
|
192
|
+
### 基本使用
|
|
193
|
+
|
|
194
|
+
```typescript
|
|
195
|
+
import { cutVideo, cutVideoByTimeRange, cutVideoByDuration, cutVideoFromStart } from '@xiping/node-utils';
|
|
196
|
+
|
|
197
|
+
// 从 30 秒开始截取 60 秒
|
|
198
|
+
const result = await cutVideo('/path/to/video.mp4', {
|
|
199
|
+
startTime: '00:00:30',
|
|
200
|
+
duration: '00:01:00',
|
|
201
|
+
outputFormat: 'mp4',
|
|
202
|
+
overwrite: true,
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// 按时间范围:1:30 到 3:45
|
|
206
|
+
const r2 = await cutVideoByTimeRange('/path/to/video.mp4', '00:01:30', '00:03:45');
|
|
207
|
+
|
|
208
|
+
// 从 2 分钟开始截取 90 秒
|
|
209
|
+
const r3 = await cutVideoByDuration('/path/to/video.mp4', '00:02:00', '00:01:30');
|
|
210
|
+
|
|
211
|
+
// 只取前 30 秒
|
|
212
|
+
const r4 = await cutVideoFromStart('/path/to/video.mp4', 30);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### CutVideoOptions
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
{
|
|
219
|
+
startTime?: string; // 开始时间,如 '00:00:30' 或秒数
|
|
220
|
+
duration?: string; // 持续时间
|
|
221
|
+
endTime?: string; // 结束时间(与 duration 二选一)
|
|
222
|
+
outputFileName?: string;
|
|
223
|
+
outputFormat?: string; // 默认 'mp4'
|
|
224
|
+
tempDir?: string;
|
|
225
|
+
overwrite?: boolean; // 是否覆盖已存在文件
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
### CutVideoResult
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
{
|
|
233
|
+
outputPath: string;
|
|
234
|
+
metadata: {
|
|
235
|
+
originalDuration: number;
|
|
236
|
+
cutDuration: number;
|
|
237
|
+
startTime: string;
|
|
238
|
+
endTime: string;
|
|
239
|
+
fileSize: number;
|
|
240
|
+
processingTime: number;
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
时间格式支持:`HH:MM:SS`、`MM:SS` 或纯秒数。输入路径需为**绝对路径**。更多说明见 [README_cutVideo.md](./README_cutVideo.md)。
|
|
246
|
+
|
|
247
|
+
---
|
|
248
|
+
|
|
249
|
+
## 4. 提取音频(extractAudio)
|
|
250
|
+
|
|
251
|
+
从视频文件中仅提取音频轨,输出为独立音频文件(mp3、m4a、wav)。
|
|
252
|
+
|
|
253
|
+
### 基本使用
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
import { extractAudio } from '@xiping/node-utils';
|
|
257
|
+
|
|
258
|
+
const result = await extractAudio('/path/to/video.mp4', {
|
|
259
|
+
outputFormat: 'mp3', // 'mp3' | 'm4a' | 'wav',默认 'mp3'
|
|
260
|
+
outputFileName: 'audio.mp3',
|
|
261
|
+
overwrite: true,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
console.log(result.outputPath);
|
|
265
|
+
console.log(result.metadata.duration, result.metadata.fileSize);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### 进度回调
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
const result = await extractAudio('/path/to/video.mp4', {
|
|
272
|
+
outputFormat: 'mp3',
|
|
273
|
+
onProgress(progress) {
|
|
274
|
+
console.log(progress.phase, progress.percent, progress.message);
|
|
275
|
+
// phase: 'preparing' | 'encoding' | 'done'
|
|
276
|
+
if (progress.currentTime != null && progress.duration != null) {
|
|
277
|
+
console.log(`已处理 ${progress.currentTime}/${progress.duration} 秒`);
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### 选项说明
|
|
284
|
+
|
|
285
|
+
| 选项 | 类型 | 默认值 | 说明 |
|
|
286
|
+
|-----------------|----------|----------|------------------------------|
|
|
287
|
+
| outputFileName | string | 源文件名.格式 | 输出文件名 |
|
|
288
|
+
| outputFormat | string | 'mp3' | mp3 / m4a / wav |
|
|
289
|
+
| overwrite | boolean | false | 是否覆盖已存在文件 |
|
|
290
|
+
| tempDir | string | - | 自定义临时目录 |
|
|
291
|
+
| copyStream | boolean | true | 尽量流复制不重编码(格式兼容时) |
|
|
292
|
+
| onProgress | function | - | 进度回调 |
|
|
293
|
+
|
|
294
|
+
### 返回值
|
|
295
|
+
|
|
296
|
+
```typescript
|
|
297
|
+
{
|
|
298
|
+
outputPath: string;
|
|
299
|
+
metadata: {
|
|
300
|
+
duration: number; // 音频时长(秒)
|
|
301
|
+
fileSize: number; // 输出文件大小(字节)
|
|
302
|
+
processingTime: number; // 处理耗时(毫秒)
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
输入路径需为**绝对路径**;若视频无音轨将抛错。使用前请确保已安装 ffmpeg。
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
## 5. 环境检查(check)
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
import { checkFFmpegAvailability, isFfprobeAvailable } from '@xiping/node-utils';
|
|
315
|
+
|
|
316
|
+
if (checkFFmpegAvailability()) {
|
|
317
|
+
console.log('ffmpeg 可用');
|
|
318
|
+
}
|
|
319
|
+
if (isFfprobeAvailable()) {
|
|
320
|
+
console.log('ffprobe 可用');
|
|
321
|
+
}
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
- **checkFFmpegAvailability()**:用于缩略图、视频截取、提取音频前检查。
|
|
325
|
+
- **isFfprobeAvailable()**:用于视频信息接口前检查。
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
## 错误处理
|
|
330
|
+
|
|
331
|
+
各函数在以下情况会抛出错误,建议用 try/catch 包裹:
|
|
332
|
+
|
|
333
|
+
- 文件不存在或路径无效
|
|
334
|
+
- 未安装 ffmpeg/ffprobe 或不可用
|
|
335
|
+
- 格式/参数不支持(如时间超出视频时长、视频无音轨)
|
|
336
|
+
- 权限或磁盘空间不足
|
|
337
|
+
|
|
338
|
+
```typescript
|
|
339
|
+
try {
|
|
340
|
+
const info = getVideoInfo('/path/to/video.mp4');
|
|
341
|
+
} catch (err) {
|
|
342
|
+
console.error(err.message);
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## 支持格式
|
|
349
|
+
|
|
350
|
+
ffprobe/ffmpeg 支持常见容器与编码,例如:
|
|
351
|
+
|
|
352
|
+
- 容器:MP4, AVI, MOV, MKV, FLV, WMV, WebM, OGV, 3GP 等
|
|
353
|
+
- 具体支持以系统安装的 FFmpeg 版本为准
|
|
354
|
+
|
|
355
|
+
---
|
|
356
|
+
|
|
357
|
+
## API 速览
|
|
358
|
+
|
|
359
|
+
| 接口 | 说明 |
|
|
360
|
+
|--------------------------|------|
|
|
361
|
+
| `getVideoInfo(path)` | 获取视频基本信息 |
|
|
362
|
+
| `getMultipleVideoInfo(paths)` | 批量获取视频信息 |
|
|
363
|
+
| `getDetailedVideoInfo(path)` | 获取详细元数据(含章节等) |
|
|
364
|
+
| `getThumbnail(path, options)` | 生成多帧缩略图(异步) |
|
|
365
|
+
| `cutVideo(path, options)` | 按选项截取视频(异步) |
|
|
366
|
+
| `cutVideoByTimeRange(path, start, end, options)` | 按起止时间截取 |
|
|
367
|
+
| `cutVideoByDuration(path, start, duration, options)` | 按起始+时长截取 |
|
|
368
|
+
| `cutVideoFromStart(path, durationSeconds, options)` | 从开头截取 N 秒 |
|
|
369
|
+
| `extractAudio(path, options)` | 从视频提取音频(异步) |
|
|
370
|
+
| `isFfprobeAvailable()` | 检测 ffprobe 是否可用 |
|
|
371
|
+
| `checkFFmpegAvailability()` | 检测 ffmpeg 是否可用 |
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Video Cutter (视频截取器)
|
|
2
|
+
|
|
3
|
+
一个基于 FFmpeg 的 Node.js 视频截取工具,支持精确的时间控制和高性能的视频处理。
|
|
4
|
+
|
|
5
|
+
## 功能特性
|
|
6
|
+
|
|
7
|
+
- 🎯 **精确时间控制** - 支持多种时间格式 (HH:MM:SS, 秒数)
|
|
8
|
+
- ⚡ **高性能处理** - 使用 FFmpeg 的 copy 模式,避免重新编码
|
|
9
|
+
- 🛡️ **输入验证** - 完整的参数验证和错误处理
|
|
10
|
+
- 📁 **灵活输出** - 支持自定义输出格式和文件名
|
|
11
|
+
- 🔄 **临时文件管理** - 自动清理临时文件
|
|
12
|
+
- 📊 **详细元数据** - 返回处理结果和文件信息
|
|
13
|
+
|
|
14
|
+
## 安装依赖
|
|
15
|
+
|
|
16
|
+
确保系统已安装 FFmpeg:
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
# macOS
|
|
20
|
+
brew install ffmpeg
|
|
21
|
+
|
|
22
|
+
# Ubuntu/Debian
|
|
23
|
+
sudo apt update
|
|
24
|
+
sudo apt install ffmpeg
|
|
25
|
+
|
|
26
|
+
# Windows
|
|
27
|
+
# 下载并安装 FFmpeg,或使用 chocolatey: choco install ffmpeg
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## API 参考
|
|
31
|
+
|
|
32
|
+
### 主要函数
|
|
33
|
+
|
|
34
|
+
#### `cutVideo(videoPath, options)`
|
|
35
|
+
|
|
36
|
+
主要的视频截取函数。
|
|
37
|
+
|
|
38
|
+
**参数:**
|
|
39
|
+
- `videoPath` (string): 输入视频文件的绝对路径
|
|
40
|
+
- `options` (CutVideoOptions): 截取选项
|
|
41
|
+
|
|
42
|
+
**返回值:**
|
|
43
|
+
- `Promise<CutVideoResult>`: 包含输出路径和元数据的对象
|
|
44
|
+
|
|
45
|
+
#### `cutVideoByTimeRange(videoPath, startTime, endTime, options)`
|
|
46
|
+
|
|
47
|
+
按时间范围截取视频的便捷函数。
|
|
48
|
+
|
|
49
|
+
#### `cutVideoByDuration(videoPath, startTime, duration, options)`
|
|
50
|
+
|
|
51
|
+
按持续时间截取视频的便捷函数。
|
|
52
|
+
|
|
53
|
+
#### `cutVideoFromStart(videoPath, durationSeconds, options)`
|
|
54
|
+
|
|
55
|
+
从视频开头截取指定秒数的便捷函数。
|
|
56
|
+
|
|
57
|
+
### 类型定义
|
|
58
|
+
|
|
59
|
+
#### CutVideoOptions
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
interface CutVideoOptions {
|
|
63
|
+
startTime?: string; // 开始时间 (格式: HH:MM:SS 或秒数)
|
|
64
|
+
duration?: string; // 持续时间 (格式: HH:MM:SS 或秒数)
|
|
65
|
+
endTime?: string; // 结束时间 (格式: HH:MM:SS 或秒数)
|
|
66
|
+
outputFileName?: string; // 输出文件名
|
|
67
|
+
outputFormat?: string; // 输出格式 (mp4, avi, mov, etc.)
|
|
68
|
+
tempDir?: string; // 临时目录
|
|
69
|
+
overwrite?: boolean; // 是否覆盖已存在的文件
|
|
70
|
+
}
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
#### CutVideoResult
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
interface CutVideoResult {
|
|
77
|
+
outputPath: string;
|
|
78
|
+
metadata: {
|
|
79
|
+
originalDuration: number; // 原视频时长(秒)
|
|
80
|
+
cutDuration: number; // 截取时长(秒)
|
|
81
|
+
startTime: string; // 开始时间
|
|
82
|
+
endTime: string; // 结束时间
|
|
83
|
+
fileSize: number; // 输出文件大小(字节)
|
|
84
|
+
processingTime: number; // 处理时间(毫秒)
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## 使用示例
|
|
90
|
+
|
|
91
|
+
### 基本用法
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { cutVideo } from './cutVideo.js';
|
|
95
|
+
|
|
96
|
+
// 从第30秒开始截取60秒
|
|
97
|
+
const result = await cutVideo('/path/to/video.mp4', {
|
|
98
|
+
startTime: '00:00:30',
|
|
99
|
+
duration: '00:01:00'
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
console.log('输出文件:', result.outputPath);
|
|
103
|
+
console.log('文件大小:', result.metadata.fileSize);
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### 按时间范围截取
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
import { cutVideoByTimeRange } from './cutVideo.js';
|
|
110
|
+
|
|
111
|
+
// 截取 1:30 到 3:45 之间的片段
|
|
112
|
+
const result = await cutVideoByTimeRange(
|
|
113
|
+
'/path/to/video.mp4',
|
|
114
|
+
'00:01:30',
|
|
115
|
+
'00:03:45'
|
|
116
|
+
);
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### 按持续时间截取
|
|
120
|
+
|
|
121
|
+
```typescript
|
|
122
|
+
import { cutVideoByDuration } from './cutVideo.js';
|
|
123
|
+
|
|
124
|
+
// 从第2分钟开始截取90秒
|
|
125
|
+
const result = await cutVideoByDuration(
|
|
126
|
+
'/path/to/video.mp4',
|
|
127
|
+
'00:02:00',
|
|
128
|
+
'00:01:30'
|
|
129
|
+
);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### 从头开始截取
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
import { cutVideoFromStart } from './cutVideo.js';
|
|
136
|
+
|
|
137
|
+
// 截取视频前30秒
|
|
138
|
+
const result = await cutVideoFromStart(
|
|
139
|
+
'/path/to/video.mp4',
|
|
140
|
+
30
|
|
141
|
+
);
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### 自定义输出选项
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const result = await cutVideo('/path/to/video.mp4', {
|
|
148
|
+
startTime: '00:01:00',
|
|
149
|
+
duration: '00:02:00',
|
|
150
|
+
outputFileName: 'my_cut_video.mp4',
|
|
151
|
+
outputFormat: 'mp4',
|
|
152
|
+
overwrite: true
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### 使用自定义临时目录
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
const result = await cutVideo('/path/to/video.mp4', {
|
|
160
|
+
startTime: '00:00:30',
|
|
161
|
+
duration: '00:01:00',
|
|
162
|
+
tempDir: '/custom/temp/directory'
|
|
163
|
+
});
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## 时间格式支持
|
|
167
|
+
|
|
168
|
+
支持多种时间格式:
|
|
169
|
+
|
|
170
|
+
- `HH:MM:SS` - 标准时间格式 (如: `01:30:45`)
|
|
171
|
+
- `MM:SS` - 分钟:秒格式 (如: `30:45`)
|
|
172
|
+
- 秒数 - 纯数字格式 (如: `90.5`)
|
|
173
|
+
|
|
174
|
+
## 错误处理
|
|
175
|
+
|
|
176
|
+
函数会抛出以下类型的错误:
|
|
177
|
+
|
|
178
|
+
- **文件不存在**: 输入视频文件不存在
|
|
179
|
+
- **FFmpeg 未安装**: 系统未安装 FFmpeg
|
|
180
|
+
- **时间参数无效**: 开始时间大于等于视频时长
|
|
181
|
+
- **输出文件已存在**: 输出文件存在且未设置覆盖选项
|
|
182
|
+
- **FFmpeg 执行失败**: 视频处理过程中出现错误
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
try {
|
|
186
|
+
const result = await cutVideo('/path/to/video.mp4', {
|
|
187
|
+
startTime: '00:00:30',
|
|
188
|
+
duration: '00:01:00'
|
|
189
|
+
});
|
|
190
|
+
} catch (error) {
|
|
191
|
+
console.error('视频截取失败:', error.message);
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## 性能优化
|
|
196
|
+
|
|
197
|
+
- 使用 FFmpeg 的 `-c copy` 参数避免重新编码
|
|
198
|
+
- 自动管理临时文件,处理完成后自动清理
|
|
199
|
+
- 支持自定义临时目录以优化 I/O 性能
|
|
200
|
+
|
|
201
|
+
## 注意事项
|
|
202
|
+
|
|
203
|
+
1. **路径要求**: 输入路径必须是绝对路径
|
|
204
|
+
2. **文件权限**: 确保对输入和输出目录有读写权限
|
|
205
|
+
3. **磁盘空间**: 确保有足够的磁盘空间存储输出文件
|
|
206
|
+
4. **FFmpeg 依赖**: 必须安装 FFmpeg 才能使用此模块
|
|
207
|
+
|
|
208
|
+
## 日志输出
|
|
209
|
+
|
|
210
|
+
模块会输出详细的处理日志,包括:
|
|
211
|
+
- 参数验证结果
|
|
212
|
+
- FFmpeg 命令执行
|
|
213
|
+
- 处理进度和结果
|
|
214
|
+
- 错误信息
|
|
215
|
+
|
|
216
|
+
日志格式:`[VideoCutter] 消息内容`
|
|
217
|
+
|
|
218
|
+
## 许可证
|
|
219
|
+
|
|
220
|
+
MIT License
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/** 提取音频进度信息,用于 onProgress 回调 */
|
|
2
|
+
export interface ExtractAudioProgress {
|
|
3
|
+
/** 当前阶段 */
|
|
4
|
+
phase: "preparing" | "encoding" | "done";
|
|
5
|
+
/** 总进度 0–100 */
|
|
6
|
+
percent: number;
|
|
7
|
+
/** 可读描述 */
|
|
8
|
+
message?: string;
|
|
9
|
+
/** 当前已处理时长(秒) */
|
|
10
|
+
currentTime?: number;
|
|
11
|
+
/** 视频总时长(秒) */
|
|
12
|
+
duration?: number;
|
|
13
|
+
}
|
|
14
|
+
export interface ExtractAudioOptions {
|
|
15
|
+
/** 输出文件名 */
|
|
16
|
+
outputFileName?: string;
|
|
17
|
+
/** 输出格式,默认 mp3 */
|
|
18
|
+
outputFormat?: "mp3" | "m4a" | "wav";
|
|
19
|
+
/** 是否覆盖已存在文件 */
|
|
20
|
+
overwrite?: boolean;
|
|
21
|
+
/** 自定义临时目录 */
|
|
22
|
+
tempDir?: string;
|
|
23
|
+
/** 是否尽量流复制不重编码(仅当格式兼容时有效,如 m4a 保留 aac) */
|
|
24
|
+
copyStream?: boolean;
|
|
25
|
+
/** 进度回调 */
|
|
26
|
+
onProgress?: (progress: ExtractAudioProgress) => void;
|
|
27
|
+
}
|
|
28
|
+
export interface ExtractAudioResult {
|
|
29
|
+
outputPath: string;
|
|
30
|
+
metadata: {
|
|
31
|
+
duration: number;
|
|
32
|
+
fileSize: number;
|
|
33
|
+
processingTime: number;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare function extractAudio(videoPath: string, options?: ExtractAudioOptions): Promise<ExtractAudioResult>;
|