@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.
@@ -1,112 +1,5 @@
1
- import shell from "shelljs";
2
1
  export * from "./getThumbnail.js";
3
2
  export * from "./check.js";
4
3
  export * from "./cutVideo.js";
5
- /**
6
- * 获取视频信息(使用 ffprobe)
7
- * @param videoPath 视频文件路径
8
- * @returns 视频信息对象
9
- */
10
- export function getVideoInfo(videoPath) {
11
- // 检查文件是否存在
12
- if (!shell.test("-f", videoPath)) {
13
- throw new Error(`视频文件不存在: ${videoPath}`);
14
- }
15
- // 使用 ffprobe 获取 JSON 格式的视频信息
16
- const result = shell.exec(`ffprobe -v quiet -print_format json -show_format -show_streams "${videoPath}"`, { silent: true });
17
- if (result.code !== 0) {
18
- throw new Error(`获取视频信息失败: ${result.stderr}`);
19
- }
20
- let probeData;
21
- try {
22
- probeData = JSON.parse(result.stdout);
23
- }
24
- catch (error) {
25
- throw new Error(`解析 ffprobe 输出失败: ${error}`);
26
- }
27
- const info = {
28
- path: videoPath,
29
- rawInfo: JSON.stringify(probeData, null, 2),
30
- };
31
- // 从 format 信息中获取时长和文件大小
32
- if (probeData.format) {
33
- if (probeData.format.duration) {
34
- info.duration = parseFloat(probeData.format.duration);
35
- info.durationFormatted = formatDuration(info.duration);
36
- }
37
- if (probeData.format.size) {
38
- info.fileSize = parseInt(probeData.format.size);
39
- info.fileSizeFormatted = formatFileSize(info.fileSize);
40
- }
41
- }
42
- // 从视频流中获取信息
43
- const videoStream = probeData.streams?.find((stream) => stream.codec_type === "video");
44
- if (videoStream) {
45
- info.videoCodec = videoStream.codec_name || "unknown";
46
- info.width = videoStream.width || 0;
47
- info.height = videoStream.height || 0;
48
- if (videoStream.r_frame_rate) {
49
- const [num, den] = videoStream.r_frame_rate.split("/");
50
- info.fps = parseInt(num) / parseInt(den);
51
- }
52
- if (videoStream.bit_rate) {
53
- info.bitrate = parseInt(videoStream.bit_rate);
54
- }
55
- }
56
- // 从音频流中获取信息
57
- const audioStream = probeData.streams?.find((stream) => stream.codec_type === "audio");
58
- if (audioStream) {
59
- info.audioCodec = audioStream.codec_name || "unknown";
60
- }
61
- return info;
62
- }
63
- /**
64
- * 格式化文件大小
65
- * @param bytes 字节数
66
- * @returns 格式化后的文件大小字符串
67
- */
68
- function formatFileSize(bytes) {
69
- const units = ["B", "KB", "MB", "GB", "TB"];
70
- let size = bytes;
71
- let unitIndex = 0;
72
- while (size >= 1024 && unitIndex < units.length - 1) {
73
- size /= 1024;
74
- unitIndex++;
75
- }
76
- return `${size.toFixed(2)} ${units[unitIndex]}`;
77
- }
78
- /**
79
- * 格式化时长
80
- * @param seconds 秒数
81
- * @returns 格式化后的时长字符串
82
- */
83
- function formatDuration(seconds) {
84
- const hours = Math.floor(seconds / 3600);
85
- const minutes = Math.floor((seconds % 3600) / 60);
86
- const secs = Math.floor(seconds % 60);
87
- const ms = Math.floor((seconds % 1) * 100);
88
- return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${secs.toString().padStart(2, "0")}.${ms.toString().padStart(2, "0")}`;
89
- }
90
- /**
91
- * 批量获取视频信息
92
- * @param videoPaths 视频文件路径数组
93
- * @returns 视频信息对象数组
94
- */
95
- export function getMultipleVideoInfo(videoPaths) {
96
- return videoPaths.map((path) => getVideoInfo(path));
97
- }
98
- /**
99
- * 获取详细的视频信息(包含更多元数据)
100
- * @param videoPath 视频文件路径
101
- * @returns 详细的视频信息
102
- */
103
- export function getDetailedVideoInfo(videoPath) {
104
- if (!shell.test("-f", videoPath)) {
105
- throw new Error(`视频文件不存在: ${videoPath}`);
106
- }
107
- const result = shell.exec(`ffprobe -v quiet -print_format json -show_format -show_streams -show_chapters -show_private_data "${videoPath}"`, { silent: true });
108
- if (result.code !== 0) {
109
- throw new Error(`获取详细视频信息失败: ${result.stderr}`);
110
- }
111
- return JSON.parse(result.stdout);
112
- }
4
+ export * from "./getVideoInfo.js";
5
+ export * from "./extractAudio.js";
@@ -0,0 +1,230 @@
1
+ # 图片格式转换工具
2
+
3
+ 基于 Sharp 的高性能图片格式转换工具,支持多种图片格式之间的转换,包括 JPG、PNG、WebP、AVIF 等。
4
+
5
+ ## 功能特性
6
+
7
+ - 🚀 高性能图片格式转换
8
+ - 📐 支持尺寸调整和缩放
9
+ - 🎨 支持质量控制和压缩选项
10
+ - 📦 批量转换支持
11
+ - 🖼️ 缩略图生成
12
+ - 📊 详细的转换统计信息
13
+ - 🔍 图片信息获取
14
+
15
+ ## 支持的格式
16
+
17
+ ### 输入格式
18
+ - JPEG/JPG
19
+ - PNG
20
+ - WebP
21
+ - AVIF
22
+ - TIFF
23
+ - GIF
24
+
25
+ ### 输出格式
26
+ - JPEG/JPG
27
+ - PNG
28
+ - WebP
29
+ - AVIF
30
+ - TIFF
31
+
32
+ ## 安装依赖
33
+
34
+ 确保项目中已安装 Sharp:
35
+
36
+ ```bash
37
+ npm install sharp
38
+ ```
39
+
40
+ ## 基本用法
41
+
42
+ ### 1. 单张图片转换
43
+
44
+ ```typescript
45
+ import { convertImage } from '@xiping/node-utils';
46
+
47
+ // 将 JPG 转换为 WebP
48
+ const result = await convertImage(
49
+ './input/sample.jpg',
50
+ './output/sample.webp',
51
+ 'webp',
52
+ {
53
+ quality: 85,
54
+ width: 800,
55
+ height: 600
56
+ }
57
+ );
58
+
59
+ console.log('转换结果:', result);
60
+ ```
61
+
62
+ ### 2. 批量转换
63
+
64
+ ```typescript
65
+ import { batchConvert } from '@xiping/node-utils';
66
+
67
+ const files = [
68
+ {
69
+ inputPath: './input/image1.jpg',
70
+ outputPath: './output/image1.webp',
71
+ format: 'webp'
72
+ },
73
+ {
74
+ inputPath: './input/image2.png',
75
+ outputPath: './output/image2.avif',
76
+ format: 'avif'
77
+ }
78
+ ];
79
+
80
+ const results = await batchConvert(files, {
81
+ quality: 80,
82
+ width: 1200
83
+ });
84
+ ```
85
+
86
+ ### 3. 创建缩略图
87
+
88
+ ```typescript
89
+ import { createThumbnail } from '@xiping/node-utils';
90
+
91
+ const result = await createThumbnail(
92
+ './input/large-image.jpg',
93
+ './output/thumbnail.jpg',
94
+ 200,
95
+ 200,
96
+ 'jpeg',
97
+ { quality: 90 }
98
+ );
99
+ ```
100
+
101
+ ### 4. 获取图片信息
102
+
103
+ ```typescript
104
+ import { getImageInfo } from '@xiping/node-utils';
105
+
106
+ const info = await getImageInfo('./input/sample.jpg');
107
+ console.log('图片信息:', info);
108
+ ```
109
+
110
+ ## 高级用法
111
+
112
+ ### 使用 ImageConverter 类
113
+
114
+ ```typescript
115
+ import { ImageConverter } from '@xiping/node-utils';
116
+
117
+ // 转换为 WebP(推荐用于 Web)
118
+ await ImageConverter.toWebP('./input.jpg', './output.webp', 85);
119
+
120
+ // 转换为 AVIF(最新格式,压缩率更高)
121
+ await ImageConverter.toAVIF('./input.jpg', './output.avif', 80);
122
+
123
+ // 转换为 PNG(保持透明度)
124
+ await ImageConverter.toPNG('./input.jpg', './output.png', true);
125
+
126
+ // 转换为 JPEG(通用格式)
127
+ await ImageConverter.toJPEG('./input.png', './output.jpg', 90);
128
+ ```
129
+
130
+ ### 创建响应式图片
131
+
132
+ ```typescript
133
+ import { ImageConverter } from '@xiping/node-utils';
134
+
135
+ const sizes = [
136
+ { width: 320, height: 240, suffix: 'small' },
137
+ { width: 640, height: 480, suffix: 'medium' },
138
+ { width: 1280, height: 960, suffix: 'large' }
139
+ ];
140
+
141
+ const results = await ImageConverter.createResponsiveImages(
142
+ './input/hero-image.jpg',
143
+ './output/',
144
+ 'hero',
145
+ sizes
146
+ );
147
+ ```
148
+
149
+ ## 转换选项
150
+
151
+ ```typescript
152
+ interface ConvertOptions {
153
+ /** 输出质量 (1-100),仅对 JPEG、WebP、AVIF 有效 */
154
+ quality?: number;
155
+ /** 是否保持透明度,仅对 PNG、WebP 有效 */
156
+ keepTransparency?: boolean;
157
+ /** 输出宽度,保持宽高比 */
158
+ width?: number;
159
+ /** 输出高度,保持宽高比 */
160
+ height?: number;
161
+ /** 是否强制调整尺寸(不保持宽高比) */
162
+ forceResize?: boolean;
163
+ /** 压缩级别 (0-9),仅对 PNG 有效 */
164
+ compressionLevel?: number;
165
+ /** 是否渐进式编码,仅对 JPEG 有效 */
166
+ progressive?: boolean;
167
+ }
168
+ ```
169
+
170
+ ## 转换结果
171
+
172
+ ```typescript
173
+ interface ConvertResult {
174
+ /** 输入文件路径 */
175
+ inputPath: string;
176
+ /** 输出文件路径 */
177
+ outputPath: string;
178
+ /** 原始文件大小(字节) */
179
+ originalSize: number;
180
+ /** 转换后文件大小(字节) */
181
+ convertedSize: number;
182
+ /** 压缩率 */
183
+ compressionRatio: number;
184
+ /** 转换耗时(毫秒) */
185
+ processingTime: number;
186
+ }
187
+ ```
188
+
189
+ ## 格式推荐
190
+
191
+ ### Web 应用
192
+ - **WebP**: 现代浏览器支持,压缩率高,推荐用于 Web
193
+ - **AVIF**: 最新格式,压缩率最高,但浏览器支持有限
194
+ - **JPEG**: 通用格式,兼容性最好
195
+
196
+ ### 移动应用
197
+ - **WebP**: Android 原生支持,iOS 14+ 支持
198
+ - **JPEG**: 通用兼容性
199
+
200
+ ### 打印/专业用途
201
+ - **PNG**: 无损压缩,支持透明度
202
+ - **TIFF**: 专业格式,支持多种压缩算法
203
+
204
+ ## 性能优化建议
205
+
206
+ 1. **批量处理**: 使用 `batchConvert` 进行批量转换
207
+ 2. **质量设置**: 根据用途调整质量参数(Web 用 80-85,打印用 90-95)
208
+ 3. **尺寸优化**: 根据显示需求设置合适的输出尺寸
209
+ 4. **格式选择**: 优先使用 WebP 或 AVIF 以获得更好的压缩率
210
+
211
+ ## 错误处理
212
+
213
+ 所有函数都会抛出详细的错误信息,建议使用 try-catch 进行错误处理:
214
+
215
+ ```typescript
216
+ try {
217
+ const result = await convertImage(inputPath, outputPath, 'webp');
218
+ console.log('转换成功:', result);
219
+ } catch (error) {
220
+ console.error('转换失败:', error.message);
221
+ }
222
+ ```
223
+
224
+ ## 注意事项
225
+
226
+ 1. 确保输入文件存在且可读
227
+ 2. 输出目录会自动创建
228
+ 3. GIF 格式暂不支持输出
229
+ 4. 大文件转换可能需要较长时间,建议在后台处理
230
+ 5. 某些格式转换可能不支持所有选项(如透明度)
@@ -0,0 +1,189 @@
1
+ # SRT到VTT转换器
2
+
3
+ 这个模块提供了在Node.js环境中将SRT字幕文件转换为WebVTT格式的功能。
4
+
5
+ ## 功能特性
6
+
7
+ - ✅ 从文件读取SRT并转换为VTT
8
+ - ✅ 支持批量转换多个SRT文件
9
+ - ✅ 支持字符串内容直接转换
10
+ - ✅ 自动验证SRT格式
11
+ - ✅ 完整的错误处理
12
+ - ✅ TypeScript支持
13
+
14
+ ## 安装
15
+
16
+ ```bash
17
+ npm install @xiping/node-utils
18
+ ```
19
+
20
+ ## 使用方法
21
+
22
+ ### 1. 从文件读取SRT并转换为VTT字符串
23
+
24
+ ```typescript
25
+ import { convertSrtFileToVtt } from '@xiping/node-utils/src/srt-to-vtt';
26
+
27
+ try {
28
+ const vttContent = convertSrtFileToVtt('./subtitles.srt');
29
+ console.log('转换后的VTT内容:', vttContent);
30
+ } catch (error) {
31
+ console.error('转换失败:', error);
32
+ }
33
+ ```
34
+
35
+ ### 2. 从文件读取SRT并保存为VTT文件
36
+
37
+ ```typescript
38
+ import { convertSrtFileToVttFile } from '@xiping/node-utils/src/srt-to-vtt';
39
+
40
+ try {
41
+ // 指定输出路径
42
+ const outputPath = convertSrtFileToVttFile('./subtitles.srt', './output/subtitles.vtt');
43
+ console.log('VTT文件已保存到:', outputPath);
44
+
45
+ // 或者使用默认输出路径(同目录,扩展名改为.vtt)
46
+ const defaultOutputPath = convertSrtFileToVttFile('./subtitles.srt');
47
+ console.log('默认输出路径:', defaultOutputPath);
48
+ } catch (error) {
49
+ console.error('文件转换失败:', error);
50
+ }
51
+ ```
52
+
53
+ ### 3. 批量转换多个SRT文件
54
+
55
+ ```typescript
56
+ import { batchConvertSrtToVtt } from '@xiping/node-utils/src/srt-to-vtt';
57
+
58
+ try {
59
+ const srtFiles = [
60
+ './subtitles1.srt',
61
+ './subtitles2.srt',
62
+ './subtitles3.srt'
63
+ ];
64
+
65
+ const results = batchConvertSrtToVtt(srtFiles, './output');
66
+
67
+ results.forEach(result => {
68
+ if (result.success) {
69
+ console.log(`✅ ${result.input} -> ${result.output}`);
70
+ } else {
71
+ console.error(`❌ ${result.input}: ${result.error}`);
72
+ }
73
+ });
74
+ } catch (error) {
75
+ console.error('批量转换失败:', error);
76
+ }
77
+ ```
78
+
79
+ ### 4. 从字符串内容直接转换
80
+
81
+ ```typescript
82
+ import { convertSrtStringToVtt } from '@xiping/node-utils/src/srt-to-vtt';
83
+
84
+ try {
85
+ const srtContent = `1
86
+ 00:00:00,000 --> 00:00:04,000
87
+ 这是第一行字幕
88
+
89
+ 2
90
+ 00:00:04,000 --> 00:00:08,000
91
+ 这是第二行字幕`;
92
+
93
+ const vttContent = convertSrtStringToVtt(srtContent);
94
+ console.log('转换后的VTT内容:', vttContent);
95
+ } catch (error) {
96
+ console.error('字符串转换失败:', error);
97
+ }
98
+ ```
99
+
100
+ ## API参考
101
+
102
+ ### convertSrtFileToVtt(srtFilePath: string): string
103
+
104
+ 从文件读取SRT内容并转换为VTT格式字符串。
105
+
106
+ **参数:**
107
+ - `srtFilePath`: SRT文件路径
108
+
109
+ **返回值:**
110
+ - 转换后的VTT内容字符串
111
+
112
+ **异常:**
113
+ - 文件不存在时抛出错误
114
+ - 文件格式无效时抛出错误
115
+
116
+ ### convertSrtFileToVttFile(srtFilePath: string, outputPath?: string): string
117
+
118
+ 从文件读取SRT内容并转换为VTT格式,同时保存到文件。
119
+
120
+ **参数:**
121
+ - `srtFilePath`: SRT文件路径
122
+ - `outputPath`: 输出VTT文件路径(可选)
123
+
124
+ **返回值:**
125
+ - 输出文件路径
126
+
127
+ ### batchConvertSrtToVtt(srtFilePaths: string[], outputDir?: string): Array<{ input: string; output: string; success: boolean; error?: string }>
128
+
129
+ 批量转换多个SRT文件到VTT格式。
130
+
131
+ **参数:**
132
+ - `srtFilePaths`: SRT文件路径数组
133
+ - `outputDir`: 输出目录(可选)
134
+
135
+ **返回值:**
136
+ - 转换结果数组,包含每个文件的转换状态
137
+
138
+ ### convertSrtStringToVtt(srtContent: string): string
139
+
140
+ 从字符串内容直接转换为VTT格式。
141
+
142
+ **参数:**
143
+ - `srtContent`: SRT内容字符串
144
+
145
+ **返回值:**
146
+ - VTT格式字符串
147
+
148
+ ## 格式说明
149
+
150
+ ### SRT格式
151
+ ```
152
+ 1
153
+ 00:00:00,000 --> 00:00:04,000
154
+ 字幕内容
155
+
156
+ 2
157
+ 00:00:04,000 --> 00:00:08,000
158
+ 字幕内容
159
+ ```
160
+
161
+ ### WebVTT格式
162
+ ```
163
+ WEBVTT
164
+
165
+ 00:00:00.000 --> 00:00:04.000
166
+ 字幕内容
167
+
168
+ 00:00:04.000 --> 00:00:08.000
169
+ 字幕内容
170
+ ```
171
+
172
+ ## 主要差异
173
+
174
+ 1. **时间格式**: SRT使用逗号分隔毫秒,VTT使用点分隔
175
+ 2. **头部**: VTT需要`WEBVTT`头部
176
+ 3. **索引**: SRT包含序号,VTT不包含
177
+
178
+ ## 错误处理
179
+
180
+ 所有函数都包含完整的错误处理:
181
+
182
+ - 文件不存在
183
+ - 文件格式错误
184
+ - 权限问题
185
+ - 编码问题
186
+
187
+ ## 示例
188
+
189
+ 查看 `example.ts` 文件获取完整的使用示例。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiping/node-utils",
3
- "version": "1.0.53",
3
+ "version": "1.0.61",
4
4
  "description": "node-utils",
5
5
  "type": "module",
6
6
  "author": "The-End-Hero <527409987@qq.com>",
@@ -20,12 +20,12 @@
20
20
  },
21
21
  "scripts": {
22
22
  "test": "echo \"Error: run tests from root\" && exit 1",
23
- "build": "tsc"
23
+ "build": "tsc && node scripts/copy-readmes.js"
24
24
  },
25
25
  "bugs": {
26
26
  "url": "https://github.com/The-End-Hero/wang-ping/issues"
27
27
  },
28
- "gitHead": "9ea632707640fbbdb62a9751402c2f66bbca7373",
28
+ "gitHead": "6cb2ac8c64a780abb18ec793978d3adfff71fba7",
29
29
  "publishConfig": {
30
30
  "access": "public",
31
31
  "registry": "https://registry.npmjs.org/"