@bililive-tools/manager 1.10.0 → 1.11.1

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.
@@ -12,7 +12,8 @@ declare class BililiveRecorderCommand extends EventEmitter {
12
12
  inputOptions(...options: string[]): BililiveRecorderCommand;
13
13
  _getArguments(): string[];
14
14
  run(): void;
15
- kill(signal?: NodeJS.Signals): void;
15
+ kill(): void;
16
+ cut(): void;
16
17
  }
17
18
  export declare const createBililiveBuilder: () => BililiveRecorderCommand;
18
19
  export declare class BililiveDownloader extends EventEmitter implements IDownloader {
@@ -28,6 +29,7 @@ export declare class BililiveDownloader extends EventEmitter implements IDownloa
28
29
  }) => string;
29
30
  readonly segment: Segment;
30
31
  readonly inputOptions: string[];
32
+ readonly disableDanma: boolean;
31
33
  readonly url: string;
32
34
  readonly debugLevel: "none" | "basic" | "verbose";
33
35
  readonly headers: {
@@ -46,5 +48,6 @@ export declare class BililiveDownloader extends EventEmitter implements IDownloa
46
48
  stop(): Promise<void>;
47
49
  getExtraDataController(): import("../xml_stream_controller.js").XmlStreamController | null;
48
50
  get videoFilePath(): string;
51
+ cut(): void;
49
52
  }
50
53
  export {};
@@ -1,5 +1,6 @@
1
1
  import EventEmitter from "node:events";
2
2
  import { spawn } from "node:child_process";
3
+ import { DEFAULT_USER_AGENT } from "./index.js";
3
4
  import { StreamManager, getBililivePath } from "../index.js";
4
5
  import { byte2MB } from "../utils.js";
5
6
  // Bililive command builder class similar to ffmpeg
@@ -46,6 +47,7 @@ class BililiveRecorderCommand extends EventEmitter {
46
47
  const bililiveExecutable = getBililivePath();
47
48
  this.process = spawn(bililiveExecutable, args, {
48
49
  stdio: ["pipe", "pipe", "pipe"],
50
+ windowsHide: true,
49
51
  });
50
52
  if (this.process.stdout) {
51
53
  this.process.stdout.on("data", (data) => {
@@ -64,7 +66,6 @@ class BililiveRecorderCommand extends EventEmitter {
64
66
  this.process.on("error", (error) => {
65
67
  this.emit("error", error);
66
68
  });
67
- [];
68
69
  this.process.on("close", (code) => {
69
70
  if (code === 0) {
70
71
  this.emit("end");
@@ -74,9 +75,15 @@ class BililiveRecorderCommand extends EventEmitter {
74
75
  }
75
76
  });
76
77
  }
77
- kill(signal = "SIGTERM") {
78
+ kill() {
78
79
  if (this.process) {
79
- this.process.kill(signal);
80
+ this.process.stdin?.write("q\n");
81
+ // this.process.kill("SIGTERM");
82
+ }
83
+ }
84
+ cut() {
85
+ if (this.process) {
86
+ this.process.stdin?.write("s\n");
80
87
  }
81
88
  }
82
89
  }
@@ -94,6 +101,7 @@ export class BililiveDownloader extends EventEmitter {
94
101
  getSavePath;
95
102
  segment;
96
103
  inputOptions = [];
104
+ disableDanma = false;
97
105
  url;
98
106
  debugLevel = "none";
99
107
  headers;
@@ -104,16 +112,20 @@ export class BililiveDownloader extends EventEmitter {
104
112
  // 存在自动分段,永远为true
105
113
  const hasSegment = true;
106
114
  this.hasSegment = hasSegment;
115
+ this.disableDanma = opts.disableDanma ?? false;
107
116
  this.debugLevel = opts.debugLevel ?? "none";
108
117
  let videoFormat = "flv";
109
- this.streamManager = new StreamManager(opts.getSavePath, hasSegment, "bililive", videoFormat, {
118
+ this.streamManager = new StreamManager(opts.getSavePath, hasSegment, this.disableDanma, "bililive", videoFormat, {
110
119
  onUpdateLiveInfo: this.onUpdateLiveInfo,
111
120
  });
112
121
  this.getSavePath = opts.getSavePath;
113
122
  this.inputOptions = [];
114
123
  this.url = opts.url;
115
124
  this.segment = opts.segment;
116
- this.headers = opts.headers;
125
+ this.headers = {
126
+ "User-Agent": DEFAULT_USER_AGENT,
127
+ ...(opts.headers || {}),
128
+ };
117
129
  this.command = this.createCommand();
118
130
  this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
119
131
  this.emit("videoFileCreated", { filename, cover, rawFilename, title });
@@ -126,11 +138,7 @@ export class BililiveDownloader extends EventEmitter {
126
138
  });
127
139
  }
128
140
  createCommand() {
129
- const inputOptions = [
130
- ...this.inputOptions,
131
- "-h",
132
- "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
133
- ];
141
+ const inputOptions = [...this.inputOptions, "--disable-log-file", "true"];
134
142
  if (this.debugLevel === "verbose") {
135
143
  inputOptions.push("-l", "Debug");
136
144
  }
@@ -187,7 +195,7 @@ export class BililiveDownloader extends EventEmitter {
187
195
  async stop() {
188
196
  try {
189
197
  // 直接发送SIGINT信号,会导致数据丢失
190
- this.command.kill("SIGINT");
198
+ this.command.kill();
191
199
  await this.streamManager.handleVideoCompleted();
192
200
  }
193
201
  catch (err) {
@@ -200,4 +208,7 @@ export class BililiveDownloader extends EventEmitter {
200
208
  get videoFilePath() {
201
209
  return this.streamManager.videoFilePath;
202
210
  }
211
+ cut() {
212
+ this.command.cut();
213
+ }
203
214
  }
@@ -18,6 +18,7 @@ export declare class FFmpegDownloader extends EventEmitter implements IDownloade
18
18
  ffmpegOutputOptions: string[];
19
19
  readonly inputOptions: string[];
20
20
  readonly isHls: boolean;
21
+ readonly disableDanma: boolean;
21
22
  readonly url: string;
22
23
  formatName: FormatName;
23
24
  videoFormat: VideoFormat;
@@ -39,4 +40,5 @@ export declare class FFmpegDownloader extends EventEmitter implements IDownloade
39
40
  stop(): Promise<void>;
40
41
  getExtraDataController(): import("../xml_stream_controller.js").XmlStreamController | null;
41
42
  get videoFilePath(): string;
43
+ cut(): void;
42
44
  }
@@ -1,6 +1,7 @@
1
1
  import EventEmitter from "node:events";
2
2
  import { createFFMPEGBuilder, StreamManager, utils } from "../index.js";
3
3
  import { createInvalidStreamChecker, assert } from "../utils.js";
4
+ import { DEFAULT_USER_AGENT } from "./index.js";
4
5
  export class FFmpegDownloader extends EventEmitter {
5
6
  onEnd;
6
7
  onUpdateLiveInfo;
@@ -14,6 +15,7 @@ export class FFmpegDownloader extends EventEmitter {
14
15
  ffmpegOutputOptions = [];
15
16
  inputOptions = [];
16
17
  isHls;
18
+ disableDanma = false;
17
19
  url;
18
20
  formatName;
19
21
  videoFormat;
@@ -50,7 +52,8 @@ export class FFmpegDownloader extends EventEmitter {
50
52
  }
51
53
  }
52
54
  this.videoFormat = videoFormat;
53
- this.streamManager = new StreamManager(opts.getSavePath, this.hasSegment, "ffmpeg", this.videoFormat, {
55
+ this.disableDanma = opts.disableDanma ?? false;
56
+ this.streamManager = new StreamManager(opts.getSavePath, this.hasSegment, this.disableDanma, "ffmpeg", this.videoFormat, {
54
57
  onUpdateLiveInfo: this.onUpdateLiveInfo,
55
58
  });
56
59
  this.timeoutChecker = utils.createTimeoutChecker(() => this.onEnd("ffmpeg timeout"), 3 * 10e3, false);
@@ -78,7 +81,7 @@ export class FFmpegDownloader extends EventEmitter {
78
81
  const inputOptions = [
79
82
  ...this.inputOptions,
80
83
  "-user_agent",
81
- "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
84
+ this.headers?.["User-Agent"] ?? DEFAULT_USER_AGENT,
82
85
  ];
83
86
  if (this.isHls) {
84
87
  inputOptions.push(...["-reconnect", "1", "-reconnect_streamed", "1", "-reconnect_delay_max", "3"]);
@@ -89,8 +92,8 @@ export class FFmpegDownloader extends EventEmitter {
89
92
  if (this.headers) {
90
93
  const headers = [];
91
94
  Object.entries(this.headers).forEach(([key, value]) => {
92
- if (!value)
93
- return;
95
+ if (!value || key === "User-Agent")
96
+ return; // User-Agent单独处理
94
97
  headers.push(`${key}:${value}`);
95
98
  });
96
99
  if (headers.length) {
@@ -186,4 +189,7 @@ export class FFmpegDownloader extends EventEmitter {
186
189
  get videoFilePath() {
187
190
  return this.streamManager.videoFilePath;
188
191
  }
192
+ cut() {
193
+ throw new Error("FFmpeg downloader does not support cut operation.");
194
+ }
189
195
  }
@@ -14,6 +14,7 @@ export interface BaseRecorderOptions {
14
14
  }) => string;
15
15
  segment: Segment;
16
16
  inputOptions?: string[];
17
+ disableDanma?: boolean;
17
18
  formatName: FormatName;
18
19
  debugLevel?: "none" | "basic" | "verbose";
19
20
  headers?: {
@@ -29,6 +30,7 @@ export interface IDownloader extends EventEmitter {
29
30
  readonly hasSegment: boolean;
30
31
  readonly segment: Segment;
31
32
  readonly inputOptions: string[];
33
+ readonly disableDanma: boolean;
32
34
  readonly url: string;
33
35
  readonly headers: {
34
36
  [key: string]: string | undefined;
@@ -39,6 +41,7 @@ export interface IDownloader extends EventEmitter {
39
41
  }) => string;
40
42
  run(): void;
41
43
  stop(): Promise<void>;
44
+ cut(): void;
42
45
  getArguments(): string[];
43
46
  getExtraDataController(): XmlStreamController | null;
44
47
  createCommand(): any;
@@ -19,6 +19,7 @@ export type RecorderOptions<T extends DownloaderType> = T extends "ffmpeg" ? FFM
19
19
  */
20
20
  export type RecorderInstance<T extends DownloaderType> = T extends "ffmpeg" ? FFmpegDownloader : T extends "mesio" ? mesioDownloader : BililiveDownloader;
21
21
  type RecorderOpts = FFMPEGRecorderOptions | MesioRecorderOptions | BililiveRecorderOptions;
22
+ export declare const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36";
22
23
  /**
23
24
  * 创建录制器的工厂函数
24
25
  */
@@ -5,6 +5,7 @@ import { parseSizeToBytes } from "../utils.js";
5
5
  export { FFmpegDownloader } from "./FFmpegDownloader.js";
6
6
  export { mesioDownloader } from "./mesioDownloader.js";
7
7
  export { BililiveDownloader } from "./BililiveDownloader.js";
8
+ export const DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36";
8
9
  /**
9
10
  * 创建录制器的工厂函数
10
11
  */
@@ -12,7 +12,7 @@ declare class MesioCommand extends EventEmitter {
12
12
  inputOptions(...options: string[]): MesioCommand;
13
13
  _getArguments(): string[];
14
14
  run(): void;
15
- kill(signal?: NodeJS.Signals): void;
15
+ kill(): void;
16
16
  }
17
17
  export declare const createMesioBuilder: () => MesioCommand;
18
18
  export declare class mesioDownloader extends EventEmitter implements IDownloader {
@@ -28,6 +28,7 @@ export declare class mesioDownloader extends EventEmitter implements IDownloader
28
28
  }) => string;
29
29
  readonly segment: Segment;
30
30
  readonly inputOptions: string[];
31
+ readonly disableDanma: boolean;
31
32
  readonly url: string;
32
33
  readonly debugLevel: "none" | "basic" | "verbose";
33
34
  readonly headers: {
@@ -43,5 +44,6 @@ export declare class mesioDownloader extends EventEmitter implements IDownloader
43
44
  stop(): Promise<void>;
44
45
  getExtraDataController(): import("../xml_stream_controller.js").XmlStreamController | null;
45
46
  get videoFilePath(): string;
47
+ cut(): void;
46
48
  }
47
49
  export {};
@@ -1,8 +1,8 @@
1
1
  import path from "node:path";
2
2
  import EventEmitter from "node:events";
3
3
  import { spawn } from "node:child_process";
4
+ import { DEFAULT_USER_AGENT } from "./index.js";
4
5
  import { StreamManager, getMesioPath } from "../index.js";
5
- // Mesio command builder class similar to ffmpeg
6
6
  class MesioCommand extends EventEmitter {
7
7
  _input = "";
8
8
  _output = "";
@@ -46,6 +46,7 @@ class MesioCommand extends EventEmitter {
46
46
  const mesioExecutable = getMesioPath();
47
47
  this.process = spawn(mesioExecutable, args, {
48
48
  stdio: ["pipe", "pipe", "pipe"],
49
+ windowsHide: true,
49
50
  });
50
51
  if (this.process.stdout) {
51
52
  this.process.stdout.on("data", (data) => {
@@ -74,9 +75,10 @@ class MesioCommand extends EventEmitter {
74
75
  }
75
76
  });
76
77
  }
77
- kill(signal = "SIGTERM") {
78
+ kill() {
78
79
  if (this.process) {
79
- this.process.kill(signal);
80
+ this.process.stdin?.write("q");
81
+ this.process.stdin?.end();
80
82
  }
81
83
  }
82
84
  }
@@ -94,6 +96,7 @@ export class mesioDownloader extends EventEmitter {
94
96
  getSavePath;
95
97
  segment;
96
98
  inputOptions = [];
99
+ disableDanma = false;
97
100
  url;
98
101
  debugLevel = "none";
99
102
  headers;
@@ -104,6 +107,7 @@ export class mesioDownloader extends EventEmitter {
104
107
  // 存在自动分段,永远为true
105
108
  const hasSegment = true;
106
109
  this.hasSegment = hasSegment;
110
+ this.disableDanma = opts.disableDanma ?? false;
107
111
  this.debugLevel = opts.debugLevel ?? "none";
108
112
  let videoFormat = "flv";
109
113
  if (opts.url.includes(".m3u8")) {
@@ -118,14 +122,17 @@ export class mesioDownloader extends EventEmitter {
118
122
  else if (opts.formatName === "flv") {
119
123
  videoFormat = "flv";
120
124
  }
121
- this.streamManager = new StreamManager(opts.getSavePath, hasSegment, "mesio", videoFormat, {
125
+ this.streamManager = new StreamManager(opts.getSavePath, hasSegment, this.disableDanma, "mesio", videoFormat, {
122
126
  onUpdateLiveInfo: this.onUpdateLiveInfo,
123
127
  });
124
128
  this.getSavePath = opts.getSavePath;
125
129
  this.inputOptions = [];
126
130
  this.url = opts.url;
127
131
  this.segment = opts.segment;
128
- this.headers = opts.headers;
132
+ this.headers = {
133
+ "User-Agent": DEFAULT_USER_AGENT,
134
+ ...(opts.headers || {}),
135
+ };
129
136
  this.command = this.createCommand();
130
137
  this.streamManager.on("videoFileCreated", ({ filename, cover, rawFilename, title }) => {
131
138
  this.emit("videoFileCreated", { filename, cover, rawFilename, title });
@@ -138,13 +145,7 @@ export class mesioDownloader extends EventEmitter {
138
145
  });
139
146
  }
140
147
  createCommand() {
141
- const inputOptions = [
142
- ...this.inputOptions,
143
- "--fix",
144
- "-H",
145
- "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36",
146
- "--no-proxy",
147
- ];
148
+ const inputOptions = [...this.inputOptions, "--fix", "--no-proxy"];
148
149
  if (this.debugLevel === "verbose") {
149
150
  inputOptions.push("-v");
150
151
  }
@@ -183,8 +184,8 @@ export class mesioDownloader extends EventEmitter {
183
184
  }
184
185
  async stop() {
185
186
  try {
186
- // 直接发送SIGINT信号,会导致数据丢失
187
- this.command.kill("SIGINT");
187
+ this.command.kill();
188
+ await new Promise((resolve) => setTimeout(resolve, 2000));
188
189
  await this.streamManager.handleVideoCompleted();
189
190
  }
190
191
  catch (err) {
@@ -197,4 +198,7 @@ export class mesioDownloader extends EventEmitter {
197
198
  get videoFilePath() {
198
199
  return this.streamManager.videoFilePath;
199
200
  }
201
+ cut() {
202
+ throw new Error("Mesio downloader does not support cut operation.");
203
+ }
200
204
  }
@@ -15,8 +15,9 @@ export declare class Segment extends EventEmitter {
15
15
  rawRecordingVideoPath: string;
16
16
  /** 输出文件名名,不包含拓展名 */
17
17
  outputVideoFilePath: string;
18
+ disableDanma: boolean;
18
19
  videoExt: TrueVideoFormat;
19
- constructor(getSavePath: GetSavePath, videoExt: TrueVideoFormat);
20
+ constructor(getSavePath: GetSavePath, disableDanma: boolean, videoExt: TrueVideoFormat);
20
21
  handleSegmentEnd(): Promise<void>;
21
22
  onSegmentStart(stderrLine: string, callBack?: {
22
23
  onUpdateLiveInfo: () => Promise<{
@@ -35,7 +36,7 @@ export declare class StreamManager extends EventEmitter {
35
36
  recorderType: RecorderType;
36
37
  private videoFormat;
37
38
  private callBack?;
38
- constructor(getSavePath: GetSavePath, hasSegment: boolean, recorderType: RecorderType, videoFormat: TrueVideoFormat, callBack?: {
39
+ constructor(getSavePath: GetSavePath, hasSegment: boolean, disableDanma: boolean, recorderType: RecorderType, videoFormat: TrueVideoFormat, callBack?: {
39
40
  onUpdateLiveInfo: () => Promise<{
40
41
  title?: string;
41
42
  cover?: string;
@@ -1,7 +1,7 @@
1
1
  import EventEmitter from "node:events";
2
2
  import fs from "fs/promises";
3
3
  import { createRecordExtraDataController } from "../xml_stream_controller.js";
4
- import { replaceExtName, ensureFolderExist, isFfmpegStartSegment, isMesioStartSegment, isBililiveStartSegment, isFfmpegStart, retry, cleanTerminalText, } from "../utils.js";
4
+ import { ensureFolderExist, isFfmpegStartSegment, isMesioStartSegment, isBililiveStartSegment, isFfmpegStart, retry, cleanTerminalText, } from "../utils.js";
5
5
  export class Segment extends EventEmitter {
6
6
  extraDataController = null;
7
7
  init = true;
@@ -10,10 +10,12 @@ export class Segment extends EventEmitter {
10
10
  rawRecordingVideoPath;
11
11
  /** 输出文件名名,不包含拓展名 */
12
12
  outputVideoFilePath;
13
+ disableDanma;
13
14
  videoExt;
14
- constructor(getSavePath, videoExt) {
15
+ constructor(getSavePath, disableDanma, videoExt) {
15
16
  super();
16
17
  this.getSavePath = getSavePath;
18
+ this.disableDanma = disableDanma;
17
19
  this.videoExt = videoExt;
18
20
  }
19
21
  async handleSegmentEnd() {
@@ -67,7 +69,9 @@ export class Segment extends EventEmitter {
67
69
  title: liveInfo?.title,
68
70
  });
69
71
  ensureFolderExist(this.outputVideoFilePath);
70
- this.extraDataController = createRecordExtraDataController(`${this.outputVideoFilePath}.xml`);
72
+ if (!this.disableDanma) {
73
+ this.extraDataController = createRecordExtraDataController(`${this.outputVideoFilePath}.xml`);
74
+ }
71
75
  // 支持两种格式的正则表达式
72
76
  // 1. FFmpeg格式: Opening 'filename' for writing
73
77
  // 2. Mesio格式: Opening FLV segment path=filename Processing
@@ -106,7 +110,7 @@ export class StreamManager extends EventEmitter {
106
110
  recorderType;
107
111
  videoFormat;
108
112
  callBack;
109
- constructor(getSavePath, hasSegment, recorderType, videoFormat, callBack) {
113
+ constructor(getSavePath, hasSegment, disableDanma, recorderType, videoFormat, callBack) {
110
114
  super();
111
115
  const recordSavePath = getSavePath({ startTime: Date.now() });
112
116
  this.recordSavePath = recordSavePath;
@@ -115,7 +119,7 @@ export class StreamManager extends EventEmitter {
115
119
  this.hasSegment = hasSegment;
116
120
  this.callBack = callBack;
117
121
  if (hasSegment) {
118
- this.segment = new Segment(getSavePath, this.videoExt);
122
+ this.segment = new Segment(getSavePath, disableDanma, this.videoExt);
119
123
  this.segment.on("DebugLog", (data) => {
120
124
  this.emit("DebugLog", data);
121
125
  });
@@ -127,8 +131,10 @@ export class StreamManager extends EventEmitter {
127
131
  });
128
132
  }
129
133
  else {
130
- const extraDataSavePath = replaceExtName(recordSavePath, ".xml");
131
- this.extraDataController = createRecordExtraDataController(extraDataSavePath);
134
+ const extraDataSavePath = `${recordSavePath}.xml`;
135
+ if (!disableDanma) {
136
+ this.extraDataController = createRecordExtraDataController(extraDataSavePath);
137
+ }
132
138
  }
133
139
  }
134
140
  async handleVideoStarted(stderrLine) {
package/lib/manager.d.ts CHANGED
@@ -77,6 +77,7 @@ export interface RecorderManager<ME extends UnknownObject, P extends RecorderPro
77
77
  recorders: Recorder<E>[];
78
78
  addRecorder: (this: RecorderManager<ME, P, PE, E>, opts: RecorderCreateOpts<E>) => Recorder<E>;
79
79
  removeRecorder: (this: RecorderManager<ME, P, PE, E>, recorder: Recorder<E>) => void;
80
+ getRecorder: (this: RecorderManager<ME, P, PE, E>, id: string) => Recorder<E> | null;
80
81
  startRecord: (this: RecorderManager<ME, P, PE, E>, id: string) => Promise<Recorder<E> | undefined>;
81
82
  stopRecord: (this: RecorderManager<ME, P, PE, E>, id: string) => Promise<Recorder<E> | undefined>;
82
83
  cutRecord: (this: RecorderManager<ME, P, PE, E>, id: string) => Promise<Recorder<E> | undefined>;
package/lib/manager.js CHANGED
@@ -194,6 +194,10 @@ export function createRecorderManager(opts) {
194
194
  delete tempBanObj[recorder.channelId];
195
195
  this.emit("RecorderRemoved", recorder.toJSON());
196
196
  },
197
+ getRecorder(id) {
198
+ const recorder = this.recorders.find((item) => item.id === id);
199
+ return recorder ?? null;
200
+ },
197
201
  async startRecord(id) {
198
202
  const recorder = this.recorders.find((item) => item.id === id);
199
203
  if (recorder == null)
package/lib/recorder.d.ts CHANGED
@@ -40,8 +40,8 @@ export interface RecorderCreateOpts<E extends AnyObject = UnknownObject> {
40
40
  formatName?: FormatName;
41
41
  /** 流编码 */
42
42
  codecName?: CodecName;
43
- /** 选择使用的api,虎牙支持: auto,web,mp,抖音支持:web,webHTML,mobile,userHTML */
44
- api?: "auto" | "web" | "mp" | "webHTML" | "mobile" | "userHTML";
43
+ /** 选择使用的api,虎牙支持: auto,web,mp,wup,抖音支持:web,webHTML,mobile,userHTML */
44
+ api?: "auto" | "web" | "mp" | "wup" | "webHTML" | "mobile" | "userHTML" | "balance" | "random" | string;
45
45
  /** 标题关键词,如果直播间标题包含这些关键词,则不会自动录制(仅对斗鱼有效),多个关键词用英文逗号分隔 */
46
46
  titleKeywords?: string;
47
47
  /** 用于指定录制文件格式,auto时,分段使用ts,不分段使用mp4 */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/manager",
3
- "version": "1.10.0",
3
+ "version": "1.11.1",
4
4
  "description": "Batch scheduling recorders",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",