@zwa73/utils 1.0.9 → 1.0.11

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.
@@ -30,7 +30,7 @@ export declare class SList<T> {
30
30
  * @param list 目标数组
31
31
  * @returns 自身
32
32
  */
33
- pushArr(list: SList<T>): this;
33
+ pushList(list: SList<T>): this;
34
34
  /**截取从起始点到结束点之前的一段数组
35
35
  * @param strat 开始点
36
36
  * @param end 结束点
@@ -58,6 +58,8 @@ export declare class SList<T> {
58
58
  contains(value: T): boolean;
59
59
  /**获取迭代器 */
60
60
  iterator(): SIterator<T>;
61
+ /**平分数组 */
62
+ divide(count: number): SList<SList<T>>;
61
63
  /**从index开始删除count个元素
62
64
  * 改变自身
63
65
  * @param index 起始点
@@ -223,7 +225,7 @@ export declare class SEntry<K, V> {
223
225
  }
224
226
  export declare class SHashMap<K, V> {
225
227
  private _map;
226
- constructor();
228
+ constructor(map?: Map<K, V>);
227
229
  /**添加一个键值对
228
230
  * @param entry 键值对
229
231
  * @returns 自身
package/dist/UtilClass.js CHANGED
@@ -51,7 +51,7 @@ class SList {
51
51
  * @param list 目标数组
52
52
  * @returns 自身
53
53
  */
54
- pushArr(list) {
54
+ pushList(list) {
55
55
  this._arr = this._arr.concat(list._arr);
56
56
  return this;
57
57
  }
@@ -97,6 +97,19 @@ class SList {
97
97
  iterator() {
98
98
  return new SIterator(this);
99
99
  }
100
+ /**平分数组 */
101
+ divide(count) {
102
+ let result = new SList();
103
+ let chunkSize = Math.ceil(this.size() / count);
104
+ for (let i = 0; i < count; i++) {
105
+ let start = i * chunkSize;
106
+ let end = (i + 1) * chunkSize;
107
+ if (end > this.size())
108
+ end = this.size();
109
+ result.push(this.slice(start, end));
110
+ }
111
+ return result;
112
+ }
100
113
  /**从index开始删除count个元素
101
114
  * 改变自身
102
115
  * @param index 起始点
@@ -403,7 +416,16 @@ class SEntry {
403
416
  exports.SEntry = SEntry;
404
417
  class SHashMap {
405
418
  _map = new Map();
406
- constructor() { }
419
+ constructor(map) {
420
+ if (map == null)
421
+ return;
422
+ let keys = map.keys();
423
+ for (let key of keys) {
424
+ let value = map.get(key);
425
+ if (value != null)
426
+ this.put(key, value);
427
+ }
428
+ }
407
429
  /**添加一个键值对
408
430
  * @param entry 键值对
409
431
  * @returns 自身
@@ -0,0 +1,7 @@
1
+ import { FfprobeData } from 'fluent-ffmpeg';
2
+ declare class SFfmpegTool {
3
+ static setFfmpegPath(ffmpegPath: string): void;
4
+ static getAudioData(inputWavPath: string): Promise<FfprobeData | null>;
5
+ }
6
+ export default SFfmpegTool;
7
+ export { SFfmpegTool };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SFfmpegTool = void 0;
4
+ const fluentFfmpeg = require("fluent-ffmpeg");
5
+ class SFfmpegTool {
6
+ static setFfmpegPath(ffmpegPath) {
7
+ fluentFfmpeg.setFfmpegPath(ffmpegPath);
8
+ }
9
+ static async getAudioData(inputWavPath) {
10
+ return new Promise((resolve, reject) => {
11
+ fluentFfmpeg.ffprobe(inputWavPath, function (err, metadata) {
12
+ if (err) {
13
+ console.log(err);
14
+ resolve(null);
15
+ }
16
+ else {
17
+ //console.log(inputWavPath+" metadata:");
18
+ //console.log(metadata);
19
+ resolve(metadata);
20
+ }
21
+ });
22
+ });
23
+ }
24
+ }
25
+ exports.SFfmpegTool = SFfmpegTool;
26
+ exports.default = SFfmpegTool;
@@ -0,0 +1,79 @@
1
+ import { FfprobeData } from "fluent-ffmpeg";
2
+ /**输入输出路径映射
3
+ * 输入路径:输入路径
4
+ */
5
+ export type IOMap = {
6
+ [key: string]: string;
7
+ };
8
+ /**ffmpeg工具类
9
+ */
10
+ declare class SFfmpegTool {
11
+ /**设置ffmpeg路径
12
+ */
13
+ static setFfmpegPath(ffmpegPath: string): void;
14
+ /**获取音频文件的元数据
15
+ * @param {string} inputWavPath - 输入音频文件路径
16
+ * @returns {Promise<FfprobeData|null>} - 返回音频文件的元数据
17
+ */
18
+ static getAudioMetaData(inputWavPath: string): Promise<FfprobeData | null>;
19
+ /**flac转ogg
20
+ * @param {string} inputFlacFile - 输入flac文件路径
21
+ * @param {string} outputOggPath - 输出ogg文件路径
22
+ * @param {number} quality - 质量
23
+ */
24
+ static flac2ogg(inputFlacFile: string, outputOggPath: string, quality?: number): Promise<boolean>;
25
+ /**wav转ogg
26
+ * @param {string} inputWavPath - 输入wav文件路径
27
+ * @param {string} outputOggPath - 输出ogg文件路径
28
+ * @param {number} quality - 质量
29
+ */
30
+ static wav2ogg(inputWavPath: string, outputOggPath: string, quality?: number): Promise<boolean>;
31
+ /**剪切音频
32
+ * @param {string} audioPath - 输入音频文件路径
33
+ * @param {string} outPath - 输出音频文件路径
34
+ * @param {number} start - 开始时间
35
+ * @param {number} time - 时长
36
+ */
37
+ static cutAudio(audioPath: string, outPath: string, start: number, time: number): Promise<boolean>;
38
+ /**删除首尾静音
39
+ * @param {string} inputWavPath - 输入wav文件路径
40
+ * @param {string} outputWavPath - 输出wav文件路径
41
+ * @param {number} threshold - 静音阈值/dB
42
+ * @param {number} silence - 保留静音时长
43
+ */
44
+ static trimSilence(inputWavPath: string, outputWavPath: string, threshold?: number, silence?: number): Promise<boolean>;
45
+ /**wav转ogg多线程
46
+ * @param {IOMap} iomap - 输入输出路径映射
47
+ * @param {number} quality - 质量
48
+ * @param {number} cpCount - 并发数
49
+ */
50
+ static wav2oggMP(iomap: IOMap, quality?: number, cpCount?: number): Promise<void>;
51
+ /**wav转ogg子线程
52
+ * @param {IOMap} iomap - 输入输出路径映射
53
+ * @param {number} quality - 质量
54
+ */
55
+ private static wav2oggCP;
56
+ /**flac转ogg多线程
57
+ * @param {IOMap} iomap - 输入输出路径映射
58
+ * @param {number} quality - 质量
59
+ * @param {number} cpCount - 并发数
60
+ */
61
+ static flac2oggMP(iomap: IOMap, quality?: number, cpCount?: number): Promise<void>;
62
+ /**flac转ogg子线程
63
+ * @param {IOMap} iomap - 输入输出路径映射
64
+ * @param {number} quality - 质量
65
+ * @param {number} cpCount - 并发数
66
+ */
67
+ private static flac2oggCP;
68
+ /**删除静音多线程
69
+ * @param {IOMap} iomap - 输入输出路径映射
70
+ * @param {number} threshold - 静音阈值/dB
71
+ * @param {number} silence - 保留静音时长
72
+ */
73
+ static trimSilenceMP(iomap: IOMap, threshold?: number, silence?: number, cpCount?: number): Promise<void>;
74
+ /**删除静音子线程
75
+ */
76
+ private static trimSilenceCP;
77
+ }
78
+ export default SFfmpegTool;
79
+ export { SFfmpegTool };
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SFfmpegTool = void 0;
4
+ const fluentFfmpeg = require("fluent-ffmpeg");
5
+ const path = require("path");
6
+ const fs = require("fs");
7
+ /**ffmpeg工具类
8
+ */
9
+ class SFfmpegTool {
10
+ /**设置ffmpeg路径
11
+ */
12
+ static setFfmpegPath(ffmpegPath) {
13
+ fluentFfmpeg.setFfmpegPath(ffmpegPath);
14
+ }
15
+ /**获取音频文件的元数据
16
+ * @param {string} inputWavPath - 输入音频文件路径
17
+ * @returns {Promise<FfprobeData|null>} - 返回音频文件的元数据
18
+ */
19
+ static async getAudioMetaData(inputWavPath) {
20
+ return new Promise((resolve, reject) => {
21
+ fluentFfmpeg.ffprobe(inputWavPath, function (err, metadata) {
22
+ if (err) {
23
+ console.log(err);
24
+ resolve(null);
25
+ }
26
+ else {
27
+ //console.log(inputWavPath+" metadata:");
28
+ //console.log(metadata);
29
+ resolve(metadata);
30
+ }
31
+ });
32
+ });
33
+ }
34
+ /**flac转ogg
35
+ * @param {string} inputFlacFile - 输入flac文件路径
36
+ * @param {string} outputOggPath - 输出ogg文件路径
37
+ * @param {number} quality - 质量
38
+ */
39
+ static async flac2ogg(inputFlacFile, outputOggPath, quality = 10) {
40
+ let wavPath = path.join(path.dirname(inputFlacFile), `tmp_${path.basename(inputFlacFile, ".flac")}.wav`);
41
+ await new Promise((resolve, reject) => {
42
+ fluentFfmpeg(inputFlacFile)
43
+ .audioCodec("pcm_s16le")
44
+ .save(wavPath)
45
+ .on("end", function () {
46
+ resolve(null);
47
+ });
48
+ });
49
+ await SFfmpegTool.wav2ogg(wavPath, outputOggPath, quality);
50
+ await new Promise((resolve, reject) => {
51
+ fs.unlink(wavPath, function (err) {
52
+ if (err)
53
+ console.error(err);
54
+ resolve(null);
55
+ });
56
+ });
57
+ return true;
58
+ }
59
+ /**wav转ogg
60
+ * @param {string} inputWavPath - 输入wav文件路径
61
+ * @param {string} outputOggPath - 输出ogg文件路径
62
+ * @param {number} quality - 质量
63
+ */
64
+ static async wav2ogg(inputWavPath, outputOggPath, quality = 10) {
65
+ //.audioQuality(10)
66
+ //.audioBitrate("192k")
67
+ //.audioChannels(channels)
68
+ //.noMetadata()
69
+ return new Promise((resolve, reject) => {
70
+ fluentFfmpeg(inputWavPath)
71
+ .audioQuality(quality)
72
+ .audioCodec("libvorbis")
73
+ .save(outputOggPath)
74
+ .on("end", () => resolve(true))
75
+ .on("error", (err) => reject(err));
76
+ });
77
+ }
78
+ /**剪切音频
79
+ * @param {string} audioPath - 输入音频文件路径
80
+ * @param {string} outPath - 输出音频文件路径
81
+ * @param {number} start - 开始时间
82
+ * @param {number} time - 时长
83
+ */
84
+ static async cutAudio(audioPath, outPath, start, time) {
85
+ return new Promise((resolve, reject) => {
86
+ fluentFfmpeg(audioPath)
87
+ .setStartTime(start)
88
+ .setDuration(time)
89
+ .save(outPath)
90
+ .on("end", () => resolve(true))
91
+ .on("error", (err) => reject(err));
92
+ });
93
+ }
94
+ /**删除首尾静音
95
+ * @param {string} inputWavPath - 输入wav文件路径
96
+ * @param {string} outputWavPath - 输出wav文件路径
97
+ * @param {number} threshold - 静音阈值/dB
98
+ * @param {number} silence - 保留静音时长
99
+ */
100
+ static async trimSilence(inputWavPath, outputWavPath, threshold = -50, silence = 0.2) {
101
+ return new Promise((resolve, reject) => {
102
+ fluentFfmpeg(inputWavPath)
103
+ .audioFilters(`silenceremove=start_periods=1:start_threshold=${threshold}dB:start_silence=${silence}`)
104
+ .audioFilters("areverse")
105
+ .audioFilters(`silenceremove=start_periods=1:start_threshold=${threshold}dB:start_silence=${silence}`)
106
+ .audioFilters("areverse")
107
+ .save(outputWavPath)
108
+ .on("end", () => resolve(true))
109
+ .on("error", (err) => reject(err));
110
+ });
111
+ }
112
+ //多线程处理
113
+ /**wav转ogg多线程
114
+ * @param {IOMap} iomap - 输入输出路径映射
115
+ * @param {number} quality - 质量
116
+ * @param {number} cpCount - 并发数
117
+ */
118
+ static async wav2oggMP(iomap, quality = 10, cpCount = 16) {
119
+ let cpList = MPClip(iomap, cpCount);
120
+ for (let cpMap of cpList)
121
+ SFfmpegTool.wav2oggCP(cpMap, quality);
122
+ }
123
+ /**wav转ogg子线程
124
+ * @param {IOMap} iomap - 输入输出路径映射
125
+ * @param {number} quality - 质量
126
+ */
127
+ static async wav2oggCP(iomap, quality = 10) {
128
+ for (let inPath in iomap) {
129
+ let outpath = iomap[inPath];
130
+ console.log("正在处理:" + outpath);
131
+ await SFfmpegTool.wav2ogg(inPath, outpath, quality);
132
+ }
133
+ }
134
+ /**flac转ogg多线程
135
+ * @param {IOMap} iomap - 输入输出路径映射
136
+ * @param {number} quality - 质量
137
+ * @param {number} cpCount - 并发数
138
+ */
139
+ static async flac2oggMP(iomap, quality = 10, cpCount = 16) {
140
+ let cpList = MPClip(iomap, cpCount);
141
+ for (let cpMap of cpList)
142
+ SFfmpegTool.flac2oggCP(cpMap, quality);
143
+ }
144
+ /**flac转ogg子线程
145
+ * @param {IOMap} iomap - 输入输出路径映射
146
+ * @param {number} quality - 质量
147
+ * @param {number} cpCount - 并发数
148
+ */
149
+ static async flac2oggCP(iomap, quality = 10) {
150
+ for (let inPath in iomap) {
151
+ let outpath = iomap[inPath];
152
+ console.log("正在处理:" + outpath);
153
+ await SFfmpegTool.flac2ogg(inPath, outpath, quality);
154
+ }
155
+ }
156
+ /**删除静音多线程
157
+ * @param {IOMap} iomap - 输入输出路径映射
158
+ * @param {number} threshold - 静音阈值/dB
159
+ * @param {number} silence - 保留静音时长
160
+ */
161
+ static async trimSilenceMP(iomap, threshold = -50, silence = 0.2, cpCount = 16) {
162
+ let cpList = MPClip(iomap, cpCount);
163
+ for (let cpMap of cpList)
164
+ SFfmpegTool.trimSilenceCP(cpMap, threshold, silence);
165
+ }
166
+ /**删除静音子线程
167
+ */
168
+ static async trimSilenceCP(iomap, threshold = -50, silence = 0.2) {
169
+ for (let inPath in iomap) {
170
+ let outpath = iomap[inPath];
171
+ console.log("正在处理:" + outpath);
172
+ await SFfmpegTool.trimSilence(inPath, outpath, threshold, silence);
173
+ }
174
+ }
175
+ }
176
+ exports.SFfmpegTool = SFfmpegTool;
177
+ /**多线程任务分割器
178
+ * @param {IOMap} iomap - 输入输出路径映射
179
+ * @param {number} cpCount - 并发数
180
+ */
181
+ function MPClip(iomap, cpCount = 16) {
182
+ let cpList = [];
183
+ for (let i = 0; i < cpCount; i++)
184
+ cpList.push({});
185
+ let cpCounter = 0;
186
+ for (let key in iomap) {
187
+ let cpIndex = cpCounter % cpCount;
188
+ cpCounter++;
189
+ cpList[cpIndex][key] = iomap[key];
190
+ }
191
+ return cpList;
192
+ }
193
+ /**ffmpeg流
194
+ */
195
+ class SFfmpegStream {
196
+ iomap;
197
+ cmdList = [];
198
+ constructor(iomap) {
199
+ this.iomap = iomap;
200
+ }
201
+ async save() { }
202
+ //执行函数
203
+ async cmdAreverse() { }
204
+ }
205
+ exports.default = SFfmpegTool;
@@ -36,3 +36,9 @@ export declare function isSafeNumber(num: number): boolean;
36
36
  * @returns {Promise<boolean>}
37
37
  */
38
38
  export declare function sleep(timeMs: number): Promise<boolean>;
39
+ /**搜索路径符合正则表达式的文件
40
+ * @param folder - 文件夹路径
41
+ * @param traitRegex - 正则表达式
42
+ * @returns {Record<string, string>} 文件名与路径的映射
43
+ */
44
+ export declare function fileSearch(folder: string, traitRegex: string): Record<string, string>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sleep = exports.isSafeNumber = exports.deepClone = exports.genUUID = exports.writeJSONFile = exports.loadJSONFile = exports.initField = exports.getTime = void 0;
3
+ exports.fileSearch = exports.sleep = exports.isSafeNumber = exports.deepClone = exports.genUUID = exports.writeJSONFile = exports.loadJSONFile = exports.initField = exports.getTime = void 0;
4
4
  const fs = require("fs");
5
5
  const crypto = require("crypto");
6
6
  const path = require("path");
@@ -116,3 +116,29 @@ function sleep(timeMs) {
116
116
  });
117
117
  }
118
118
  exports.sleep = sleep;
119
+ /**搜索路径符合正则表达式的文件
120
+ * @param folder - 文件夹路径
121
+ * @param traitRegex - 正则表达式
122
+ * @returns {Record<string, string>} 文件名与路径的映射
123
+ */
124
+ function fileSearch(folder, traitRegex) {
125
+ let outMap = {};
126
+ let subFiles = fs.readdirSync(folder);
127
+ let regex = new RegExp(traitRegex);
128
+ for (let subFile of subFiles) {
129
+ let subFilePath = path.join(folder, subFile);
130
+ subFilePath.replace(/\\/g, "/");
131
+ let stat = fs.lstatSync(subFilePath);
132
+ //判断是否是文件夹,递归调用
133
+ if (stat.isDirectory()) {
134
+ let subMap = fileSearch(path.join(subFilePath, "/"), traitRegex);
135
+ for (let key in subMap)
136
+ outMap[key] = subMap[key];
137
+ continue;
138
+ }
139
+ if (regex.test(subFilePath))
140
+ outMap[subFile] = subFilePath;
141
+ }
142
+ return outMap;
143
+ }
144
+ exports.fileSearch = fileSearch;
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export * from './UtilInterfaces';
3
3
  export * from './UtilClass';
4
4
  export * from './UtilCom';
5
5
  export * from './UtilCodecs';
6
+ export * from './UtilFfmpegTools';
package/dist/index.js CHANGED
@@ -19,3 +19,4 @@ __exportStar(require("./UtilInterfaces"), exports);
19
19
  __exportStar(require("./UtilClass"), exports);
20
20
  __exportStar(require("./UtilCom"), exports);
21
21
  __exportStar(require("./UtilCodecs"), exports);
22
+ __exportStar(require("./UtilFfmpegTools"), exports);
package/input.wav ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zwa73/utils",
3
- "version": "1.0.9",
3
+ "version": "1.0.11",
4
4
  "description": "my utils",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -14,12 +14,14 @@
14
14
  "dependencies": {
15
15
  "esm": "^3.2.25",
16
16
  "esm-resolve": "^1.0.8",
17
+ "fluent-ffmpeg": "^2.1.2",
17
18
  "html-entities": "^2.3.3",
18
19
  "http-proxy-agent": "^5.0.0",
19
20
  "https-proxy-agent": "^5.0.1",
20
21
  "tiktoken": "^1.0.7"
21
22
  },
22
23
  "devDependencies": {
24
+ "@types/fluent-ffmpeg": "^2.1.21",
23
25
  "@types/node": "^18.16.3"
24
26
  }
25
27
  }
package/src/UtilClass.ts CHANGED
@@ -55,7 +55,7 @@ export class SList<T> {
55
55
  * @param list 目标数组
56
56
  * @returns 自身
57
57
  */
58
- pushArr(list: SList<T>) {
58
+ pushList(list: SList<T>) {
59
59
  this._arr = this._arr.concat(list._arr);
60
60
  return this;
61
61
  }
@@ -109,6 +109,20 @@ export class SList<T> {
109
109
  return new SIterator<T>(this);
110
110
  }
111
111
 
112
+ /**平分数组 */
113
+ divide(count:number):SList<SList<T>>{
114
+ let result = new SList<SList<T>>();
115
+ let chunkSize = Math.ceil(this.size()/count);
116
+ for(let i=0;i<count;i++){
117
+ let start = i*chunkSize;
118
+ let end = (i+1)*chunkSize;
119
+ if(end>this.size())
120
+ end = this.size();
121
+ result.push(this.slice(start,end));
122
+ }
123
+ return result;
124
+ }
125
+
112
126
  /**从index开始删除count个元素
113
127
  * 改变自身
114
128
  * @param index 起始点
@@ -449,7 +463,16 @@ export class SEntry<K,V>{
449
463
  }
450
464
  export class SHashMap<K,V>{
451
465
  private _map:Map<K,V> = new Map();
452
- constructor(){}
466
+ constructor(map?:Map<K,V>){
467
+ if(map==null)
468
+ return;
469
+ let keys = map.keys();
470
+ for(let key of keys){
471
+ let value = map.get(key);
472
+ if(value!=null)
473
+ this.put(key,value);
474
+ }
475
+ }
453
476
  /**添加一个键值对
454
477
  * @param entry 键值对
455
478
  * @returns 自身
@@ -0,0 +1,253 @@
1
+ import { FfmpegCommand, FfprobeData } from "fluent-ffmpeg";
2
+ import * as fluentFfmpeg from "fluent-ffmpeg";
3
+ import * as path from "path";
4
+ import * as fs from "fs";
5
+
6
+ /**输入输出路径映射
7
+ * 输入路径:输入路径
8
+ */
9
+ export type IOMap = { [key: string]: string };
10
+
11
+ /**ffmpeg工具类
12
+ */
13
+ class SFfmpegTool {
14
+ /**设置ffmpeg路径
15
+ */
16
+ static setFfmpegPath(ffmpegPath: string) {
17
+ fluentFfmpeg.setFfmpegPath(ffmpegPath);
18
+ }
19
+ /**获取音频文件的元数据
20
+ * @param {string} inputWavPath - 输入音频文件路径
21
+ * @returns {Promise<FfprobeData|null>} - 返回音频文件的元数据
22
+ */
23
+ static async getAudioMetaData(
24
+ inputWavPath: string
25
+ ): Promise<FfprobeData | null> {
26
+ return new Promise((resolve, reject) => {
27
+ fluentFfmpeg.ffprobe(inputWavPath, function (err, metadata) {
28
+ if (err) {
29
+ console.log(err);
30
+ resolve(null);
31
+ } else {
32
+ //console.log(inputWavPath+" metadata:");
33
+ //console.log(metadata);
34
+ resolve(metadata);
35
+ }
36
+ });
37
+ });
38
+ }
39
+ /**flac转ogg
40
+ * @param {string} inputFlacFile - 输入flac文件路径
41
+ * @param {string} outputOggPath - 输出ogg文件路径
42
+ * @param {number} quality - 质量
43
+ */
44
+ static async flac2ogg(
45
+ inputFlacFile: string,
46
+ outputOggPath: string,
47
+ quality: number = 10
48
+ ): Promise<boolean> {
49
+ let wavPath = path.join(
50
+ path.dirname(inputFlacFile),
51
+ `tmp_${path.basename(inputFlacFile, ".flac")}.wav`
52
+ );
53
+ await new Promise((resolve, reject) => {
54
+ fluentFfmpeg(inputFlacFile)
55
+ .audioCodec("pcm_s16le")
56
+ .save(wavPath)
57
+ .on("end", function () {
58
+ resolve(null);
59
+ });
60
+ });
61
+ await SFfmpegTool.wav2ogg(wavPath, outputOggPath, quality);
62
+ await new Promise((resolve, reject) => {
63
+ fs.unlink(wavPath, function (err) {
64
+ if (err) console.error(err);
65
+ resolve(null);
66
+ });
67
+ });
68
+ return true;
69
+ }
70
+ /**wav转ogg
71
+ * @param {string} inputWavPath - 输入wav文件路径
72
+ * @param {string} outputOggPath - 输出ogg文件路径
73
+ * @param {number} quality - 质量
74
+ */
75
+ static async wav2ogg(
76
+ inputWavPath: string,
77
+ outputOggPath: string,
78
+ quality: number = 10
79
+ ): Promise<boolean> {
80
+ //.audioQuality(10)
81
+ //.audioBitrate("192k")
82
+ //.audioChannels(channels)
83
+ //.noMetadata()
84
+ return new Promise((resolve, reject) => {
85
+ fluentFfmpeg(inputWavPath)
86
+ .audioQuality(quality)
87
+ .audioCodec("libvorbis")
88
+ .save(outputOggPath)
89
+ .on("end", () => resolve(true))
90
+ .on("error", (err) => reject(err));
91
+ });
92
+ }
93
+ /**剪切音频
94
+ * @param {string} audioPath - 输入音频文件路径
95
+ * @param {string} outPath - 输出音频文件路径
96
+ * @param {number} start - 开始时间
97
+ * @param {number} time - 时长
98
+ */
99
+ static async cutAudio(
100
+ audioPath: string,
101
+ outPath: string,
102
+ start: number,
103
+ time: number
104
+ ): Promise<boolean> {
105
+ return new Promise((resolve, reject) => {
106
+ fluentFfmpeg(audioPath)
107
+ .setStartTime(start)
108
+ .setDuration(time)
109
+ .save(outPath)
110
+ .on("end", () => resolve(true))
111
+ .on("error", (err) => reject(err));
112
+ });
113
+ }
114
+ /**删除首尾静音
115
+ * @param {string} inputWavPath - 输入wav文件路径
116
+ * @param {string} outputWavPath - 输出wav文件路径
117
+ * @param {number} threshold - 静音阈值/dB
118
+ * @param {number} silence - 保留静音时长
119
+ */
120
+ static async trimSilence(
121
+ inputWavPath: string,
122
+ outputWavPath: string,
123
+ threshold: number = -50,
124
+ silence: number = 0.2
125
+ ): Promise<boolean> {
126
+ return new Promise((resolve, reject) => {
127
+ fluentFfmpeg(inputWavPath)
128
+ .audioFilters(
129
+ `silenceremove=start_periods=1:start_threshold=${threshold}dB:start_silence=${silence}`
130
+ )
131
+ .audioFilters("areverse")
132
+ .audioFilters(
133
+ `silenceremove=start_periods=1:start_threshold=${threshold}dB:start_silence=${silence}`
134
+ )
135
+ .audioFilters("areverse")
136
+ .save(outputWavPath)
137
+ .on("end", () => resolve(true))
138
+ .on("error", (err) => reject(err));
139
+ });
140
+ }
141
+ //多线程处理
142
+ /**wav转ogg多线程
143
+ * @param {IOMap} iomap - 输入输出路径映射
144
+ * @param {number} quality - 质量
145
+ * @param {number} cpCount - 并发数
146
+ */
147
+ static async wav2oggMP(iomap: IOMap, quality = 10, cpCount = 16) {
148
+ let cpList = MPClip(iomap, cpCount);
149
+ for (let cpMap of cpList) SFfmpegTool.wav2oggCP(cpMap, quality);
150
+ }
151
+ /**wav转ogg子线程
152
+ * @param {IOMap} iomap - 输入输出路径映射
153
+ * @param {number} quality - 质量
154
+ */
155
+ private static async wav2oggCP(iomap: IOMap, quality: number = 10) {
156
+ for (let inPath in iomap) {
157
+ let outpath = iomap[inPath];
158
+ console.log("正在处理:" + outpath);
159
+ await SFfmpegTool.wav2ogg(inPath, outpath, quality);
160
+ }
161
+ }
162
+ /**flac转ogg多线程
163
+ * @param {IOMap} iomap - 输入输出路径映射
164
+ * @param {number} quality - 质量
165
+ * @param {number} cpCount - 并发数
166
+ */
167
+ static async flac2oggMP(
168
+ iomap: IOMap,
169
+ quality: number = 10,
170
+ cpCount: number = 16
171
+ ) {
172
+ let cpList = MPClip(iomap, cpCount);
173
+ for (let cpMap of cpList) SFfmpegTool.flac2oggCP(cpMap, quality);
174
+ }
175
+ /**flac转ogg子线程
176
+ * @param {IOMap} iomap - 输入输出路径映射
177
+ * @param {number} quality - 质量
178
+ * @param {number} cpCount - 并发数
179
+ */
180
+ private static async flac2oggCP(iomap: IOMap, quality: number = 10) {
181
+ for (let inPath in iomap) {
182
+ let outpath = iomap[inPath];
183
+ console.log("正在处理:" + outpath);
184
+ await SFfmpegTool.flac2ogg(inPath, outpath, quality);
185
+ }
186
+ }
187
+ /**删除静音多线程
188
+ * @param {IOMap} iomap - 输入输出路径映射
189
+ * @param {number} threshold - 静音阈值/dB
190
+ * @param {number} silence - 保留静音时长
191
+ */
192
+ static async trimSilenceMP(
193
+ iomap: IOMap,
194
+ threshold: number = -50,
195
+ silence: number = 0.2,
196
+ cpCount: number = 16
197
+ ) {
198
+ let cpList = MPClip(iomap, cpCount);
199
+ for (let cpMap of cpList)
200
+ SFfmpegTool.trimSilenceCP(cpMap, threshold, silence);
201
+ }
202
+ /**删除静音子线程
203
+ */
204
+ private static async trimSilenceCP(
205
+ iomap: IOMap,
206
+ threshold: number = -50,
207
+ silence: number = 0.2
208
+ ) {
209
+ for (let inPath in iomap) {
210
+ let outpath = iomap[inPath];
211
+ console.log("正在处理:" + outpath);
212
+ await SFfmpegTool.trimSilence(inPath, outpath, threshold, silence);
213
+ }
214
+ }
215
+ }
216
+
217
+ /**多线程任务分割器
218
+ * @param {IOMap} iomap - 输入输出路径映射
219
+ * @param {number} cpCount - 并发数
220
+ */
221
+ function MPClip(iomap: IOMap, cpCount: number = 16) {
222
+ let cpList: Array<IOMap> = [];
223
+ for (let i = 0; i < cpCount; i++) cpList.push({});
224
+ let cpCounter = 0;
225
+ for (let key in iomap) {
226
+ let cpIndex = cpCounter % cpCount;
227
+ cpCounter++;
228
+ cpList[cpIndex][key] = iomap[key];
229
+ }
230
+ return cpList;
231
+ }
232
+
233
+ type SFfmpegCmd = {
234
+ name: string;
235
+ opt: string | null;
236
+ };
237
+
238
+ /**ffmpeg流
239
+ */
240
+ class SFfmpegStream {
241
+ iomap;
242
+ cmdList: Array<SFfmpegCmd> = [];
243
+ constructor(iomap: IOMap) {
244
+ this.iomap = iomap;
245
+ }
246
+ async save() {}
247
+
248
+ //执行函数
249
+ private async cmdAreverse() {}
250
+ }
251
+
252
+ export default SFfmpegTool;
253
+ export { SFfmpegTool };
@@ -111,3 +111,28 @@ export function sleep(timeMs:number):Promise<boolean>{
111
111
  });
112
112
  }
113
113
 
114
+ /**搜索路径符合正则表达式的文件
115
+ * @param folder - 文件夹路径
116
+ * @param traitRegex - 正则表达式
117
+ * @returns {Record<string, string>} 文件名与路径的映射
118
+ */
119
+ export function fileSearch(folder: string, traitRegex: string) {
120
+ let outMap: Record<string, string> = {};
121
+ let subFiles = fs.readdirSync(folder);
122
+ let regex = new RegExp(traitRegex);
123
+ for (let subFile of subFiles) {
124
+ let subFilePath = path.join(folder, subFile);
125
+ subFilePath.replace(/\\/g,"/");
126
+ let stat = fs.lstatSync(subFilePath);
127
+
128
+ //判断是否是文件夹,递归调用
129
+ if (stat.isDirectory()) {
130
+ let subMap = fileSearch(path.join(subFilePath, "/"), traitRegex);
131
+ for (let key in subMap) outMap[key] = subMap[key];
132
+ continue;
133
+ }
134
+ if (regex.test(subFilePath))
135
+ outMap[subFile] = subFilePath;
136
+ }
137
+ return outMap;
138
+ }
package/src/index.ts CHANGED
@@ -2,4 +2,5 @@ export * from './UtilFunctions';
2
2
  export * from './UtilInterfaces';
3
3
  export * from './UtilClass';
4
4
  export * from './UtilCom';
5
- export * from './UtilCodecs';
5
+ export * from './UtilCodecs';
6
+ export * from './UtilFfmpegTools';
package/test.js CHANGED
@@ -1,4 +1,4 @@
1
- let {SList,SHashMap,SEntry} = require('./dist');
1
+ let {SList,SHashMap,SEntry,SFfmpegTool} = require('./dist');
2
2
 
3
3
  let slist = new SList([1,2,3,3,4,5,6]);
4
4
  slist.each(val => console.log(val));
@@ -10,3 +10,10 @@ map.put("123",456)
10
10
  map.put("789",111)
11
11
  for(let {key,value} of map)
12
12
  console.log(key,value)
13
+
14
+ SFfmpegTool.setFfmpegPath("E:/系统工具/ffmpeg-master-latest-win64-gpl/bin/ffmpeg.exe");
15
+ (async function(){
16
+ let data = await SFfmpegTool.getAudioMetaData("input.wav");
17
+ console.log(data);
18
+ }())
19
+