@zwa73/utils 1.0.8 → 1.0.10
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/dist/UtilClass.d.ts +4 -2
- package/dist/UtilClass.js +24 -2
- package/dist/UtilCodecs.js +21 -8
- package/dist/UtilFfmpegTool.d.ts +7 -0
- package/dist/UtilFfmpegTool.js +26 -0
- package/dist/UtilFfmpegTools.d.ts +79 -0
- package/dist/UtilFfmpegTools.js +205 -0
- package/dist/UtilFunctions.d.ts +6 -0
- package/dist/UtilFunctions.js +26 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/input.wav +0 -0
- package/package.json +3 -1
- package/publish.bat +2 -0
- package/src/UtilClass.ts +25 -2
- package/src/UtilCodecs.ts +26 -10
- package/src/UtilFfmpegTools.ts +253 -0
- package/src/UtilFunctions.ts +24 -0
- package/src/index.ts +2 -1
- package/test.js +8 -1
package/dist/UtilClass.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare class SList<T> {
|
|
|
30
30
|
* @param list 目标数组
|
|
31
31
|
* @returns 自身
|
|
32
32
|
*/
|
|
33
|
-
|
|
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
|
-
|
|
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 自身
|
package/dist/UtilCodecs.js
CHANGED
|
@@ -3,8 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.decodeTokenDavinci = exports.decodeTokenTurbo = exports.encodeTokenDavinci = exports.encodeTokenTurbo = exports.tokenNumDavinci = exports.tokenNumTurbo = exports.encodeHtmlEntities = exports.decodeHtmlEntities = void 0;
|
|
4
4
|
const he = require("html-entities");
|
|
5
5
|
const tiktoken_1 = require("tiktoken");
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
let encoderTurbo = null;
|
|
7
|
+
let encoderDavinci = null;
|
|
8
8
|
const textDecoder = new TextDecoder();
|
|
9
9
|
// 定义一个对象,存储常见的HTML实体和对应的字符
|
|
10
10
|
let htmlEntities = {
|
|
@@ -46,13 +46,21 @@ exports.encodeHtmlEntities = encodeHtmlEntities;
|
|
|
46
46
|
//cl100k_base ChatGPT models, text-embedding-ada-002
|
|
47
47
|
//p50k_base Code models, text-davinci-002, text-davinci-003
|
|
48
48
|
//r50k_base (or gpt2) GPT-3 models like davinci
|
|
49
|
+
//避免在nextjs调用时出错
|
|
50
|
+
function initTikTokenEncoder() {
|
|
51
|
+
if (encoderTurbo != null && encoderDavinci != null)
|
|
52
|
+
return;
|
|
53
|
+
encoderTurbo = (0, tiktoken_1.get_encoding)("cl100k_base");
|
|
54
|
+
encoderDavinci = (0, tiktoken_1.get_encoding)("p50k_base");
|
|
55
|
+
}
|
|
49
56
|
/**token长度计算器 Turbo模型
|
|
50
57
|
* @param {string} str = 所要计算的消息
|
|
51
58
|
* @returns {number} 整数长度结果
|
|
52
59
|
*/
|
|
53
60
|
function tokenNumTurbo(str) {
|
|
61
|
+
initTikTokenEncoder();
|
|
54
62
|
//return encoder.encode(str).length
|
|
55
|
-
return encoderTurbo
|
|
63
|
+
return encoderTurbo?.encode(str).length;
|
|
56
64
|
}
|
|
57
65
|
exports.tokenNumTurbo = tokenNumTurbo;
|
|
58
66
|
/**token长度计算器 Davinci模型
|
|
@@ -60,7 +68,8 @@ exports.tokenNumTurbo = tokenNumTurbo;
|
|
|
60
68
|
* @returns {number} 整数长度结果
|
|
61
69
|
*/
|
|
62
70
|
function tokenNumDavinci(str) {
|
|
63
|
-
|
|
71
|
+
initTikTokenEncoder();
|
|
72
|
+
return encoderDavinci?.encode(str).length;
|
|
64
73
|
}
|
|
65
74
|
exports.tokenNumDavinci = tokenNumDavinci;
|
|
66
75
|
/**token编码 Turbo模型
|
|
@@ -68,7 +77,8 @@ exports.tokenNumDavinci = tokenNumDavinci;
|
|
|
68
77
|
* @returns {Array<number>} Token数组
|
|
69
78
|
*/
|
|
70
79
|
function encodeTokenTurbo(str) {
|
|
71
|
-
|
|
80
|
+
initTikTokenEncoder();
|
|
81
|
+
return encoderTurbo?.encode(str);
|
|
72
82
|
}
|
|
73
83
|
exports.encodeTokenTurbo = encodeTokenTurbo;
|
|
74
84
|
/**token编码 Davinci模型
|
|
@@ -76,7 +86,8 @@ exports.encodeTokenTurbo = encodeTokenTurbo;
|
|
|
76
86
|
* @returns {Array<number>} Token数组
|
|
77
87
|
*/
|
|
78
88
|
function encodeTokenDavinci(str) {
|
|
79
|
-
|
|
89
|
+
initTikTokenEncoder();
|
|
90
|
+
return encoderDavinci?.encode(str);
|
|
80
91
|
}
|
|
81
92
|
exports.encodeTokenDavinci = encodeTokenDavinci;
|
|
82
93
|
/**token解码 Turbo模型
|
|
@@ -84,7 +95,8 @@ exports.encodeTokenDavinci = encodeTokenDavinci;
|
|
|
84
95
|
* @returns {string} 消息字符串
|
|
85
96
|
*/
|
|
86
97
|
function decodeTokenTurbo(arr) {
|
|
87
|
-
|
|
98
|
+
initTikTokenEncoder();
|
|
99
|
+
return textDecoder.decode(encoderTurbo?.decode(arr));
|
|
88
100
|
}
|
|
89
101
|
exports.decodeTokenTurbo = decodeTokenTurbo;
|
|
90
102
|
/**token解码 Davinci模型
|
|
@@ -92,6 +104,7 @@ exports.decodeTokenTurbo = decodeTokenTurbo;
|
|
|
92
104
|
* @returns {string} 消息字符串
|
|
93
105
|
*/
|
|
94
106
|
function decodeTokenDavinci(arr) {
|
|
95
|
-
|
|
107
|
+
initTikTokenEncoder();
|
|
108
|
+
return textDecoder.decode(encoderDavinci?.decode(arr));
|
|
96
109
|
}
|
|
97
110
|
exports.decodeTokenDavinci = decodeTokenDavinci;
|
|
@@ -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;
|
package/dist/UtilFunctions.d.ts
CHANGED
|
@@ -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>;
|
package/dist/UtilFunctions.js
CHANGED
|
@@ -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,28 @@ 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
|
+
let stat = fs.lstatSync(subFilePath);
|
|
131
|
+
//判断是否是文件夹,递归调用
|
|
132
|
+
if (stat.isDirectory()) {
|
|
133
|
+
let subMap = fileSearch(path.join(subFilePath, "/"), traitRegex);
|
|
134
|
+
for (let key in subMap)
|
|
135
|
+
outMap[key] = subMap[key];
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (regex.test(subFilePath))
|
|
139
|
+
outMap[subFile] = subFilePath;
|
|
140
|
+
}
|
|
141
|
+
return outMap;
|
|
142
|
+
}
|
|
143
|
+
exports.fileSearch = fileSearch;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
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.
|
|
3
|
+
"version": "1.0.10",
|
|
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/publish.bat
ADDED
package/src/UtilClass.ts
CHANGED
|
@@ -55,7 +55,7 @@ export class SList<T> {
|
|
|
55
55
|
* @param list 目标数组
|
|
56
56
|
* @returns 自身
|
|
57
57
|
*/
|
|
58
|
-
|
|
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 自身
|
package/src/UtilCodecs.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as he from 'html-entities';
|
|
2
|
-
import {get_encoding} from 'tiktoken';
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import {get_encoding,Tiktoken} from 'tiktoken';
|
|
3
|
+
let encoderTurbo:Tiktoken|null = null;
|
|
4
|
+
let encoderDavinci:Tiktoken|null = null;
|
|
5
5
|
const textDecoder = new TextDecoder();
|
|
6
6
|
|
|
7
7
|
|
|
@@ -48,20 +48,32 @@ export function encodeHtmlEntities(str:string) {
|
|
|
48
48
|
//p50k_base Code models, text-davinci-002, text-davinci-003
|
|
49
49
|
//r50k_base (or gpt2) GPT-3 models like davinci
|
|
50
50
|
|
|
51
|
+
|
|
52
|
+
//避免在nextjs调用时出错
|
|
53
|
+
function initTikTokenEncoder (){
|
|
54
|
+
if(encoderTurbo!=null && encoderDavinci!=null)
|
|
55
|
+
return;
|
|
56
|
+
|
|
57
|
+
encoderTurbo = get_encoding("cl100k_base");
|
|
58
|
+
encoderDavinci = get_encoding("p50k_base");
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
/**token长度计算器 Turbo模型
|
|
52
62
|
* @param {string} str = 所要计算的消息
|
|
53
63
|
* @returns {number} 整数长度结果
|
|
54
64
|
*/
|
|
55
|
-
export function tokenNumTurbo(str:string){
|
|
65
|
+
export function tokenNumTurbo(str:string):number{
|
|
66
|
+
initTikTokenEncoder();
|
|
56
67
|
//return encoder.encode(str).length
|
|
57
|
-
return encoderTurbo
|
|
68
|
+
return encoderTurbo?.encode(str).length as any as number;
|
|
58
69
|
}
|
|
59
70
|
/**token长度计算器 Davinci模型
|
|
60
71
|
* @param {string} str = 所要计算的消息
|
|
61
72
|
* @returns {number} 整数长度结果
|
|
62
73
|
*/
|
|
63
74
|
export function tokenNumDavinci(str:string):number{
|
|
64
|
-
|
|
75
|
+
initTikTokenEncoder();
|
|
76
|
+
return encoderDavinci?.encode(str).length as any as number;
|
|
65
77
|
}
|
|
66
78
|
|
|
67
79
|
/**token编码 Turbo模型
|
|
@@ -69,26 +81,30 @@ export function tokenNumDavinci(str:string):number{
|
|
|
69
81
|
* @returns {Array<number>} Token数组
|
|
70
82
|
*/
|
|
71
83
|
export function encodeTokenTurbo(str:string):Uint32Array{
|
|
72
|
-
|
|
84
|
+
initTikTokenEncoder();
|
|
85
|
+
return encoderTurbo?.encode(str) as any as Uint32Array
|
|
73
86
|
}
|
|
74
87
|
/**token编码 Davinci模型
|
|
75
88
|
* @param {string} str = 所要计算的消息
|
|
76
89
|
* @returns {Array<number>} Token数组
|
|
77
90
|
*/
|
|
78
91
|
export function encodeTokenDavinci(str:string):Uint32Array{
|
|
79
|
-
|
|
92
|
+
initTikTokenEncoder();
|
|
93
|
+
return encoderDavinci?.encode(str) as any as Uint32Array;
|
|
80
94
|
}
|
|
81
95
|
/**token解码 Turbo模型
|
|
82
96
|
* @param {Array<number>} arr = Token数组
|
|
83
97
|
* @returns {string} 消息字符串
|
|
84
98
|
*/
|
|
85
99
|
export function decodeTokenTurbo(arr:Uint32Array):string{
|
|
86
|
-
|
|
100
|
+
initTikTokenEncoder();
|
|
101
|
+
return textDecoder.decode(encoderTurbo?.decode(arr));
|
|
87
102
|
}
|
|
88
103
|
/**token解码 Davinci模型
|
|
89
104
|
* @param {Array<number>} arr = Token数组
|
|
90
105
|
* @returns {string} 消息字符串
|
|
91
106
|
*/
|
|
92
107
|
export function decodeTokenDavinci(arr:Uint32Array):string{
|
|
93
|
-
|
|
108
|
+
initTikTokenEncoder();
|
|
109
|
+
return textDecoder.decode(encoderDavinci?.decode(arr));
|
|
94
110
|
}
|
|
@@ -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 };
|
package/src/UtilFunctions.ts
CHANGED
|
@@ -111,3 +111,27 @@ 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
|
+
let stat = fs.lstatSync(subFilePath);
|
|
126
|
+
|
|
127
|
+
//判断是否是文件夹,递归调用
|
|
128
|
+
if (stat.isDirectory()) {
|
|
129
|
+
let subMap = fileSearch(path.join(subFilePath, "/"), traitRegex);
|
|
130
|
+
for (let key in subMap) outMap[key] = subMap[key];
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (regex.test(subFilePath))
|
|
134
|
+
outMap[subFile] = subFilePath;
|
|
135
|
+
}
|
|
136
|
+
return outMap;
|
|
137
|
+
}
|
package/src/index.ts
CHANGED
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
|
+
|