@zwa73/utils 1.0.187 → 1.0.189

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.
@@ -53,6 +53,10 @@ declare class SFfmpegTool {
53
53
  * @param outputWavPath - 输出wav文件路径
54
54
  */
55
55
  static resample(inputWavPath: string, outputWavPath: string, rate?: number): Promise<boolean>;
56
+ /**检查WAV文件是否为单声道
57
+ * @param filePath - 要检查的WAV文件路径
58
+ */
59
+ static isMono(filePath: string): Promise<boolean>;
56
60
  /**wav转ogg多线程
57
61
  * @param ioMap - 输入输出路径映射
58
62
  * @param quality - 质量
@@ -32,6 +32,7 @@ const pathe_1 = __importDefault(require("pathe"));
32
32
  const fs = __importStar(require("fs"));
33
33
  const UtilLogger_1 = require("./UtilLogger");
34
34
  const UtilClass_1 = require("./UtilClass");
35
+ const QuickFunction_1 = require("./QuickFunction");
35
36
  /**ffmpeg工具类
36
37
  */
37
38
  class SFfmpegTool {
@@ -73,13 +74,7 @@ class SFfmpegTool {
73
74
  const wavPath = pathe_1.default.join(pathe_1.default.dirname(inputFlacFile), `tmp_${pathe_1.default.basename(inputFlacFile, ".flac")}.wav`);
74
75
  await SFfmpegTool.flac2wav(inputFlacFile, wavPath);
75
76
  await SFfmpegTool.wav2ogg(wavPath, outputOggPath, quality);
76
- await new Promise((resolve, reject) => {
77
- fs.unlink(wavPath, function (err) {
78
- if (err)
79
- UtilLogger_1.SLogger.error("SFfmpegTool.flac2ogg unlink 错误", err);
80
- resolve(null);
81
- });
82
- });
77
+ await fs.promises.unlink(wavPath);
83
78
  return true;
84
79
  }
85
80
  /**flac转wav
@@ -192,6 +187,19 @@ class SFfmpegTool {
192
187
  });
193
188
  });
194
189
  }
190
+ /**检查WAV文件是否为单声道
191
+ * @param filePath - 要检查的WAV文件路径
192
+ */
193
+ static async isMono(filePath) {
194
+ const metadata = await SFfmpegTool.getAudioMetaData(filePath);
195
+ if (metadata == null)
196
+ (0, QuickFunction_1.throwError)("SFfmpegTool.isMono 获取音频元数据失败");
197
+ // 检查音频流的声道数
198
+ const audioStream = metadata.streams.find(stream => stream.codec_type === 'audio');
199
+ if (audioStream == null)
200
+ (0, QuickFunction_1.throwError)("SFfmpegTool.isMono 未找到音频流");
201
+ return audioStream.channels === 1;
202
+ }
195
203
  //多线程处理
196
204
  /**wav转ogg多线程
197
205
  * @param ioMap - 输入输出路径映射
@@ -1,4 +1,4 @@
1
- import { PRecord, AnyFunc, ExtractOutcome, IJData, JObject, JToken, Keyable, Literal, Matchable, MatchableFlag, Outcome, ReqVerifyFn, ProperSubsetCheck, UnionToIntersection, AnyRecord, AllExtends } from "./UtilInterfaces";
1
+ import { PRecord, AnyFunc, ExtractOutcome, IJData, JObject, JToken, Keyable, Literal, Matchable, MatchableFlag, Outcome, ReqVerifyFn, ProperSubsetCheck, UnionToIntersection, AnyRecord, AllExtends, SrtSegment } from "./UtilInterfaces";
2
2
  import { LogLevel } from "./UtilLogger";
3
3
  import { Failed, FailedLike, None, StatusSymbol, Success, SuccessLike, Timeout } from "./UtilSymbol";
4
4
  declare const HashMethodList: readonly ["md5", "sha1", "sha256", "sha512", "sha3", "blake2", "blake3"];
@@ -303,5 +303,13 @@ export declare class UtilFunc {
303
303
  * @returns 返回一个新的函数,这个函数在调用时会尝试从缓存中获取结果,如果缓存不存在或已过期,就会调用原函数并缓存其结果。
304
304
  */
305
305
  static memoize<T extends (...args: any[]) => any>(fn: T, expiry?: number): T extends (...args: infer In) => any ? AllExtends<In, JToken> extends true ? T : never : never;
306
+ /**将hh:mm:ss,ms格式转换为毫秒 */
307
+ static parseSrtTime(time: string): number;
308
+ /**将毫秒转换为hh:mm:ss,ms格式 */
309
+ static formatSrtTime(milliseconds: number): string;
310
+ /**解析srt文本为SrtSegment[] */
311
+ static parseSrt(srtText: string): SrtSegment[];
312
+ /**转换json为srt文本 */
313
+ static createSrt(segments: SrtSegment[]): string;
306
314
  }
307
315
  export {};
@@ -188,7 +188,7 @@ class UtilFunc {
188
188
  //根据最大重试次数限制进行循环
189
189
  for (let i = 0; i < count;) {
190
190
  if (i > 0 && opt.tryDelay)
191
- await this.sleep(opt.tryDelay);
191
+ await UtilFunc.sleep(opt.tryDelay);
192
192
  UtilLogger_1.SLogger.info(`开始第 ${i + 1} 次 repeatPromise`);
193
193
  //如果 plist 中当前下标的任务还未创建 则 创建当前任务
194
194
  if (plist.length < i + 1) {
@@ -396,11 +396,12 @@ class UtilFunc {
396
396
  */
397
397
  static queueProc(flag, task) {
398
398
  // 如果当前标签的队列不存在,则创建一个新的队列
399
- if (!this.pendingMap[flag])
400
- this.pendingMap[flag] = [];
399
+ if (!UtilFunc.pendingMap[flag])
400
+ UtilFunc.pendingMap[flag] = [];
401
401
  // 创建一个新的Promise,并保存resolve函数以便后续调用
402
402
  let resolveFunc;
403
- const promise = new Promise((resolve) => {
403
+ let rejectFunc;
404
+ const promise = new Promise((resolve, reject) => {
404
405
  resolveFunc = resolve;
405
406
  });
406
407
  // 定义处理任务的函数
@@ -414,25 +415,26 @@ class UtilFunc {
414
415
  }
415
416
  catch (error) {
416
417
  // 如果任务执行出错,记录错误日志
417
- UtilLogger_1.SLogger.warn(`queueProc 错误: `, error, `flag: ${String(flag)}`);
418
+ UtilLogger_1.SLogger.warn(`queueProc 错误 flag: ${String(flag)} 已抛出`);
419
+ rejectFunc(error);
418
420
  }
419
421
  finally {
420
422
  // 无论任务是否成功,都从队列中移除当前任务
421
- this.pendingMap[flag].shift();
423
+ UtilFunc.pendingMap[flag].shift();
422
424
  // 如果队列中还有任务,执行下一个任务
423
- if (this.pendingMap[flag].length > 0) {
424
- this.pendingMap[flag][0]();
425
+ if (UtilFunc.pendingMap[flag].length > 0) {
426
+ UtilFunc.pendingMap[flag][0]();
425
427
  }
426
428
  else {
427
429
  // 如果队列中没有任务,删除队列
428
- delete this.pendingMap[flag];
430
+ delete UtilFunc.pendingMap[flag];
429
431
  }
430
432
  }
431
433
  };
432
434
  // 将处理任务的函数添加到队列中
433
- this.pendingMap[flag].push(processTask);
435
+ UtilFunc.pendingMap[flag].push(processTask);
434
436
  // 如果队列中只有当前任务,立即执行
435
- if (this.pendingMap[flag].length === 1)
437
+ if (UtilFunc.pendingMap[flag].length === 1)
436
438
  processTask();
437
439
  // 返回Promise,以便调用者可以等待任务完成
438
440
  return promise;
@@ -441,7 +443,7 @@ class UtilFunc {
441
443
  * @param flag - 队列标签
442
444
  */
443
445
  static queueLength(flag) {
444
- const pd = this.pendingMap[flag];
446
+ const pd = UtilFunc.pendingMap[flag];
445
447
  return pd != null ? pd.length : 0;
446
448
  }
447
449
  /**创建一个Outcome */
@@ -724,6 +726,49 @@ class UtilFunc {
724
726
  return result;
725
727
  });
726
728
  }
729
+ /**将hh:mm:ss,ms格式转换为毫秒 */
730
+ static parseSrtTime(time) {
731
+ const [hours, minutes, seconds] = time.split(':');
732
+ const [secs, milliseconds] = seconds.split(',');
733
+ return parseInt(hours) * 3600000 +
734
+ parseInt(minutes) * 60000 +
735
+ parseInt(secs) * 1000 +
736
+ parseInt(milliseconds);
737
+ }
738
+ /**将毫秒转换为hh:mm:ss,ms格式 */
739
+ static formatSrtTime(milliseconds) {
740
+ const hours = Math.floor(milliseconds / 3_600_000);
741
+ const minutes = Math.floor((milliseconds % 3_600_000) / 60_000);
742
+ const seconds = Math.floor((milliseconds % 60_000) / 1000);
743
+ const ms = Math.floor(milliseconds % 1000);
744
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')},${String(ms).padStart(3, '0')}`;
745
+ }
746
+ /**解析srt文本为SrtSegment[] */
747
+ static parseSrt(srtText) {
748
+ const segments = [];
749
+ const srtLines = srtText.split('\n');
750
+ let index = 0;
751
+ while (index < srtLines.length) {
752
+ const id = srtLines[index++].trim();
753
+ if (!id)
754
+ continue;
755
+ const timeRange = srtLines[index++].trim();
756
+ const [start, end] = timeRange.split(' --> ')
757
+ .map(time => UtilFunc.parseSrtTime(time));
758
+ let text = '';
759
+ while (index < srtLines.length && srtLines[index].trim())
760
+ text += `${srtLines[index++]}\n`;
761
+ index++;
762
+ segments.push({ start, end, text: text.trim() });
763
+ }
764
+ return segments;
765
+ }
766
+ /**转换json为srt文本 */
767
+ static createSrt(segments) {
768
+ return segments.map((segment, index) => {
769
+ return `${index + 1}\n${UtilFunc.formatSrtTime(segment.start)} --> ${UtilFunc.formatSrtTime(segment.end)}\n${segment.text}\n`;
770
+ }).join('\n');
771
+ }
727
772
  }
728
773
  exports.UtilFunc = UtilFunc;
729
774
  __decorate([
@@ -166,4 +166,13 @@ export type CmtTuple<T extends {
166
166
  }, Tuple extends unknown[] = []> = unknown extends T[Tuple['length']] ? T & Tuple : CmtTuple<T, [...Tuple, T[Tuple['length']]]>;
167
167
  /**非严格模式下将会判断为false的值, 不包含NaN */
168
168
  export type Flasy = false | 0 | -0 | "" | null | undefined;
169
+ /**srt段 */
170
+ export type SrtSegment = {
171
+ /**开始时间, 以毫秒为单位 */
172
+ start: number;
173
+ /**结束时间, 以毫秒为单位 */
174
+ end: number;
175
+ /**字幕文本 */
176
+ text: string;
177
+ };
169
178
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zwa73/utils",
3
- "version": "1.0.187",
3
+ "version": "1.0.189",
4
4
  "description": "my utils",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -4,6 +4,7 @@ import path from "pathe";
4
4
  import * as fs from "fs";
5
5
  import { SLogger } from "@/src/UtilLogger";
6
6
  import { Stream } from "./UtilClass";
7
+ import { throwError } from "./QuickFunction";
7
8
 
8
9
  /**输入输出路径映射
9
10
  * 输入路径:输入路径
@@ -59,12 +60,7 @@ class SFfmpegTool {
59
60
  );
60
61
  await SFfmpegTool.flac2wav(inputFlacFile, wavPath);
61
62
  await SFfmpegTool.wav2ogg(wavPath, outputOggPath, quality);
62
- await new Promise((resolve, reject) => {
63
- fs.unlink(wavPath, function (err) {
64
- if (err) SLogger.error("SFfmpegTool.flac2ogg unlink 错误",err);
65
- resolve(null);
66
- });
67
- });
63
+ await fs.promises.unlink(wavPath);
68
64
  return true;
69
65
  }
70
66
 
@@ -202,6 +198,18 @@ class SFfmpegTool {
202
198
  });
203
199
  }
204
200
 
201
+ /**检查WAV文件是否为单声道
202
+ * @param filePath - 要检查的WAV文件路径
203
+ */
204
+ static async isMono(filePath: string): Promise<boolean> {
205
+ const metadata = await SFfmpegTool.getAudioMetaData(filePath);
206
+ if (metadata==null) throwError("SFfmpegTool.isMono 获取音频元数据失败");
207
+ // 检查音频流的声道数
208
+ const audioStream = metadata!.streams.find(stream => stream.codec_type === 'audio');
209
+ if (audioStream==null) throwError("SFfmpegTool.isMono 未找到音频流");
210
+ return audioStream!.channels === 1;
211
+ }
212
+
205
213
  //多线程处理
206
214
  /**wav转ogg多线程
207
215
  * @param ioMap - 输入输出路径映射
@@ -1,5 +1,5 @@
1
1
  import * as crypto from "crypto";
2
- import { PRecord, AnyFunc, ExtractOutcome, IJData, JObject, JToken, Keyable, Literal, Matchable, MatchableFlag, Outcome, ReqStat, ReqVerifyFn, ProperSubsetCheck, UnionToIntersection, AnyRecord, AllExtends } from "@/src/UtilInterfaces";
2
+ import { PRecord, AnyFunc, ExtractOutcome, IJData, JObject, JToken, Keyable, Literal, Matchable, MatchableFlag, Outcome, ReqStat, ReqVerifyFn, ProperSubsetCheck, UnionToIntersection, AnyRecord, AllExtends, SrtSegment } from "@/src/UtilInterfaces";
3
3
  import * as cp from "child_process";
4
4
  import { LogLevel, SLogger } from "@/src/UtilLogger";
5
5
  import { Completed, Failed, FailedLike, None, StatusSymbol, Success, SuccessLike, Terminated, Timeout } from "./UtilSymbol";
@@ -249,7 +249,7 @@ Promise<RepeatPromiseResult<T>>{
249
249
  try{
250
250
  //根据最大重试次数限制进行循环
251
251
  for(let i=0;i<count;){
252
- if(i>0 && opt.tryDelay) await this.sleep(opt.tryDelay);
252
+ if(i>0 && opt.tryDelay) await UtilFunc.sleep(opt.tryDelay);
253
253
  SLogger.info(`开始第 ${i+1} 次 repeatPromise`);
254
254
  //如果 plist 中当前下标的任务还未创建 则 创建当前任务
255
255
  if(plist.length<i+1){
@@ -468,11 +468,12 @@ static pendingMap:Record<Keyable,AnyFunc[]> = {};
468
468
  */
469
469
  static queueProc<T>(flag: Keyable, task: () => Promise<T>): Promise<T> {
470
470
  // 如果当前标签的队列不存在,则创建一个新的队列
471
- if (!this.pendingMap[flag]) this.pendingMap[flag] = [];
471
+ if (!UtilFunc.pendingMap[flag]) UtilFunc.pendingMap[flag] = [];
472
472
 
473
473
  // 创建一个新的Promise,并保存resolve函数以便后续调用
474
474
  let resolveFunc: (value: T | PromiseLike<T>) => void;
475
- const promise = new Promise<T>((resolve) => {
475
+ let rejectFunc: (value: any | PromiseLike<any>) => void;
476
+ const promise = new Promise<T>((resolve,reject) => {
476
477
  resolveFunc = resolve;
477
478
  });
478
479
 
@@ -486,24 +487,25 @@ static queueProc<T>(flag: Keyable, task: () => Promise<T>): Promise<T> {
486
487
  resolveFunc(result);
487
488
  } catch (error) {
488
489
  // 如果任务执行出错,记录错误日志
489
- SLogger.warn(`queueProc 错误: `,error,`flag: ${String(flag)}`);
490
+ SLogger.warn(`queueProc 错误 flag: ${String(flag)} 已抛出`);
491
+ rejectFunc(error);
490
492
  } finally {
491
493
  // 无论任务是否成功,都从队列中移除当前任务
492
- this.pendingMap[flag].shift();
494
+ UtilFunc.pendingMap[flag].shift();
493
495
  // 如果队列中还有任务,执行下一个任务
494
- if (this.pendingMap[flag].length > 0) {
495
- this.pendingMap[flag][0]();
496
+ if (UtilFunc.pendingMap[flag].length > 0) {
497
+ UtilFunc.pendingMap[flag][0]();
496
498
  } else {
497
499
  // 如果队列中没有任务,删除队列
498
- delete this.pendingMap[flag];
500
+ delete UtilFunc.pendingMap[flag];
499
501
  }
500
502
  }
501
503
  };
502
504
 
503
505
  // 将处理任务的函数添加到队列中
504
- this.pendingMap[flag].push(processTask);
506
+ UtilFunc.pendingMap[flag].push(processTask);
505
507
  // 如果队列中只有当前任务,立即执行
506
- if (this.pendingMap[flag].length === 1) processTask();
508
+ if (UtilFunc.pendingMap[flag].length === 1) processTask();
507
509
 
508
510
  // 返回Promise,以便调用者可以等待任务完成
509
511
  return promise;
@@ -512,7 +514,7 @@ static queueProc<T>(flag: Keyable, task: () => Promise<T>): Promise<T> {
512
514
  * @param flag - 队列标签
513
515
  */
514
516
  static queueLength(flag:Keyable){
515
- const pd = this.pendingMap[flag]
517
+ const pd = UtilFunc.pendingMap[flag]
516
518
  return pd!=null ? pd.length : 0;
517
519
  }
518
520
 
@@ -855,4 +857,55 @@ static memoize<T extends (...args:any[])=>any> (fn: T, expiry = Infinity):
855
857
  }
856
858
 
857
859
 
860
+
861
+ /**将hh:mm:ss,ms格式转换为毫秒 */
862
+ static parseSrtTime(time: string): number {
863
+ const [hours, minutes, seconds] = time.split(':');
864
+ const [secs, milliseconds] = seconds.split(',');
865
+
866
+ return parseInt(hours) * 3600000 +
867
+ parseInt(minutes) * 60000 +
868
+ parseInt(secs) * 1000 +
869
+ parseInt(milliseconds);
870
+ }
871
+ /**将毫秒转换为hh:mm:ss,ms格式 */
872
+ static formatSrtTime(milliseconds: number): string {
873
+ const hours = Math.floor(milliseconds / 3_600_000);
874
+ const minutes = Math.floor((milliseconds % 3_600_000) / 60_000);
875
+ const seconds = Math.floor((milliseconds % 60_000) / 1000);
876
+ const ms = Math.floor(milliseconds % 1000);
877
+
878
+ return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')},${String(ms).padStart(3, '0')}`;
879
+ }
880
+ /**解析srt文本为SrtSegment[] */
881
+ static parseSrt(srtText: string): SrtSegment[] {
882
+ const segments: SrtSegment[] = [];
883
+ const srtLines = srtText.split('\n');
884
+
885
+ let index = 0;
886
+ while (index < srtLines.length) {
887
+ const id = srtLines[index++].trim();
888
+ if (!id) continue;
889
+
890
+ const timeRange = srtLines[index++].trim();
891
+ const [start, end] = timeRange.split(' --> ')
892
+ .map(time => UtilFunc.parseSrtTime(time));
893
+
894
+ let text = '';
895
+ while (index < srtLines.length && srtLines[index].trim())
896
+ text += `${srtLines[index++]}\n`;
897
+ index++;
898
+
899
+ segments.push({ start, end, text: text.trim() });
900
+ }
901
+
902
+ return segments;
903
+ }
904
+ /**转换json为srt文本 */
905
+ static createSrt(segments: SrtSegment[]): string {
906
+ return segments.map((segment, index) => {
907
+ return `${index + 1}\n${UtilFunc.formatSrtTime(segment.start)} --> ${UtilFunc.formatSrtTime(segment.end)}\n${segment.text}\n`;
908
+ }).join('\n');
909
+ }
910
+
858
911
  }
@@ -250,4 +250,15 @@ export type CmtTuple<T extends {[K:number]:unknown},Tuple extends unknown[]=[]>
250
250
  : CmtTuple<T,[...Tuple,T[Tuple['length']]]>;
251
251
 
252
252
  /**非严格模式下将会判断为false的值, 不包含NaN */
253
- export type Flasy = false | 0 | -0 | "" | null | undefined;
253
+ export type Flasy = false | 0 | -0 | "" | null | undefined;
254
+
255
+
256
+ /**srt段 */
257
+ export type SrtSegment = {
258
+ /**开始时间, 以毫秒为单位 */
259
+ start: number;
260
+ /**结束时间, 以毫秒为单位 */
261
+ end : number;
262
+ /**字幕文本 */
263
+ text : string;
264
+ };