@bililive-tools/huya-recorder 1.3.2 → 1.7.0

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/README.md CHANGED
@@ -46,6 +46,7 @@ interface Options {
46
46
  saveCover?: boolean; // 保存封面
47
47
  api?: "auto" | "mp" | "web"; // 默认为auto,在星秀区使用mp接口,其他使用web接口,你也可以强制指定
48
48
  videoFormat?: "auto"; // 视频格式: "auto", "ts", "mkv" ,auto模式下, 分段使用 "ts",不分段使用 "mp4"
49
+ recorderType?: "auto" | "ffmpeg" | "mesio"; // 底层录制器,使用mesio时videoFormat参数无效
49
50
  }
50
51
  ```
51
52
 
package/lib/huya_api.d.ts CHANGED
@@ -1,5 +1,9 @@
1
+ import type { Recorder } from "@bililive-tools/manager";
1
2
  import type { StreamProfile } from "./types.js";
2
- export declare function getRoomInfo(roomIdOrShortId: string, formatPriorities?: Array<"flv" | "hls">): Promise<{
3
+ export declare function getRoomInfo(roomIdOrShortId: string, opts?: {
4
+ formatPriorities?: Array<"flv" | "hls">;
5
+ quality?: Recorder["quality"];
6
+ }): Promise<{
3
7
  living: boolean;
4
8
  id: number;
5
9
  owner: string;
package/lib/huya_api.js CHANGED
@@ -5,7 +5,7 @@ import { initInfo } from "./anticode.js";
5
5
  const requester = axios.create({
6
6
  timeout: 10e3,
7
7
  });
8
- export async function getRoomInfo(roomIdOrShortId, formatPriorities = ["flv", "hls"]) {
8
+ export async function getRoomInfo(roomIdOrShortId, opts = {}) {
9
9
  const res = await requester.get(`https://www.huya.com/${roomIdOrShortId}`);
10
10
  const html = res.data;
11
11
  const match = html.match(/var hyPlayerConfig = ({[^]+?};)/);
@@ -18,6 +18,7 @@ export async function getRoomInfo(roomIdOrShortId, formatPriorities = ["flv", "h
18
18
  desc: info.sDisplayName,
19
19
  bitRate: info.iBitRate,
20
20
  }));
21
+ streams.push({ desc: "真原画", bitRate: -1 });
21
22
  const data = hyPlayerConfig.stream.data[0];
22
23
  assert(data, `Unexpected resp, data is null`);
23
24
  const sources = {
@@ -35,9 +36,13 @@ export async function getRoomInfo(roomIdOrShortId, formatPriorities = ["flv", "h
35
36
  // }));
36
37
  for (const item of data?.gameStreamInfoList ?? []) {
37
38
  if (item.sFlvAntiCode && item.sFlvAntiCode.length > 0) {
39
+ let sStreamName = item.sStreamName;
40
+ if (opts.quality === -1) {
41
+ sStreamName = sStreamName.replace("-imgplus", "");
42
+ }
38
43
  const url = initInfo({
39
44
  baseUrl: item.sFlvUrl,
40
- sStreamName: item.sStreamName,
45
+ sStreamName: sStreamName,
41
46
  antiCode: item.sFlvAntiCode,
42
47
  suffix: item.sFlvUrlSuffix,
43
48
  _sessionId: Date.now(),
@@ -48,9 +53,13 @@ export async function getRoomInfo(roomIdOrShortId, formatPriorities = ["flv", "h
48
53
  });
49
54
  }
50
55
  if (item.sHlsAntiCode && item.sHlsAntiCode.length > 0) {
56
+ let sStreamName = item.sStreamName;
57
+ if (opts.quality === -1) {
58
+ sStreamName = sStreamName.replace("-imgplus", "");
59
+ }
51
60
  const url = initInfo({
52
61
  baseUrl: item.sHlsUrl,
53
- sStreamName: item.sStreamName,
62
+ sStreamName: sStreamName,
54
63
  antiCode: item.sHlsAntiCode,
55
64
  suffix: item.sHlsUrlSuffix,
56
65
  _sessionId: Date.now(),
@@ -62,7 +71,7 @@ export async function getRoomInfo(roomIdOrShortId, formatPriorities = ["flv", "h
62
71
  }
63
72
  }
64
73
  const startTime = new Date(data.gameLiveInfo?.startTime * 1000);
65
- const formatSources = getFormatSources(sources, formatPriorities);
74
+ const formatSources = getFormatSources(sources, opts.formatPriorities);
66
75
  return {
67
76
  living: vMultiStreamInfo.length > 0 && data.gameStreamInfoList.length > 0,
68
77
  id: data.gameLiveInfo.profileRoom,
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import path from "node:path";
2
2
  import mitt from "mitt";
3
- import { defaultFromJSON, defaultToJSON, genRecorderUUID, genRecordUUID, utils, FFMPEGRecorder, } from "@bililive-tools/manager";
3
+ import { defaultFromJSON, defaultToJSON, genRecorderUUID, genRecordUUID, utils, createBaseRecorder, } from "@bililive-tools/manager";
4
4
  import { getInfo, getStream } from "./stream.js";
5
5
  import { ensureFolderExist } from "./utils.js";
6
6
  import HuYaDanMu from "huya-danma-listener";
@@ -77,10 +77,16 @@ const ffmpegInputOptions = [
77
77
  const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isManualStart, }) {
78
78
  if (this.recordHandle != null)
79
79
  return this.recordHandle;
80
- const liveInfo = await getInfo(this.channelId);
81
- const { living, owner, title } = liveInfo;
82
- this.liveInfo = liveInfo;
83
- if (liveInfo.liveId === banLiveId) {
80
+ try {
81
+ const liveInfo = await getInfo(this.channelId);
82
+ this.liveInfo = liveInfo;
83
+ }
84
+ catch (error) {
85
+ this.state = "check-error";
86
+ throw error;
87
+ }
88
+ const { living, owner, title } = this.liveInfo;
89
+ if (this.liveInfo.liveId === banLiveId) {
84
90
  this.tempStopIntervalCheck = true;
85
91
  }
86
92
  else {
@@ -114,7 +120,9 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
114
120
  });
115
121
  }
116
122
  catch (err) {
117
- this.state = "idle";
123
+ if (this.qualityRetry > 0)
124
+ this.qualityRetry -= 1;
125
+ this.state = "check-error";
118
126
  throw err;
119
127
  }
120
128
  this.state = "recording";
@@ -135,12 +143,13 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
135
143
  isEnded = true;
136
144
  this.emit("DebugLog", {
137
145
  type: "common",
138
- text: `ffmpeg end, reason: ${JSON.stringify(args, (_, v) => (v instanceof Error ? v.stack : v))}`,
146
+ text: `record end, reason: ${JSON.stringify(args, (_, v) => (v instanceof Error ? v.stack : v))}`,
139
147
  });
140
148
  const reason = args[0] instanceof Error ? args[0].message : String(args[0]);
141
149
  this.recordHandle?.stop(reason);
142
150
  };
143
- const recorder = new FFMPEGRecorder({
151
+ let recorderType = this.recorderType === "mesio" ? "mesio" : "ffmpeg";
152
+ const recorder = createBaseRecorder(recorderType, {
144
153
  url: stream.url,
145
154
  outputOptions: ffmpegOutputOptions,
146
155
  inputOptions: ffmpegInputOptions,
@@ -175,7 +184,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
175
184
  extraDataController?.setMeta({
176
185
  room_id: this.channelId,
177
186
  platform: provider?.id,
178
- liveStartTimestamp: liveInfo.startTime?.getTime(),
187
+ liveStartTimestamp: this?.liveInfo?.startTime?.getTime(),
179
188
  // recordStopTimestamp: Date.now(),
180
189
  title: title,
181
190
  user_name: owner,
package/lib/stream.d.ts CHANGED
@@ -12,6 +12,7 @@ export declare function getInfo(channelId: string): Promise<{
12
12
  }>;
13
13
  export declare function getStream(opts: Pick<Recorder, "channelId" | "quality" | "streamPriorities" | "sourcePriorities" | "api" | "formatPriorities"> & {
14
14
  strictQuality?: boolean;
15
+ api?: "web" | "mp" | "auto";
15
16
  }): Promise<{
16
17
  currentStream: {
17
18
  name: string;
package/lib/stream.js CHANGED
@@ -18,7 +18,10 @@ export async function getInfo(channelId) {
18
18
  }
19
19
  async function getRoomInfo(channelId, options) {
20
20
  if (options.api == "auto") {
21
- const info = await getRoomInfoByWeb(channelId, options.formatPriorities);
21
+ const info = await getRoomInfoByWeb(channelId, {
22
+ formatPriorities: options.formatPriorities,
23
+ quality: options.quality,
24
+ });
22
25
  if (info.gid == 1663) {
23
26
  return getRoomInfoByMobile(channelId, options.formatPriorities);
24
27
  }
@@ -28,7 +31,10 @@ async function getRoomInfo(channelId, options) {
28
31
  return getRoomInfoByMobile(channelId, options.formatPriorities);
29
32
  }
30
33
  else if (options.api == "web") {
31
- return getRoomInfoByWeb(channelId, options.formatPriorities);
34
+ return getRoomInfoByWeb(channelId, {
35
+ formatPriorities: options.formatPriorities,
36
+ quality: options.quality,
37
+ });
32
38
  }
33
39
  assert(false, "Invalid api");
34
40
  }
@@ -36,6 +42,7 @@ export async function getStream(opts) {
36
42
  const info = await getRoomInfo(opts.channelId, {
37
43
  api: opts.api ?? "auto",
38
44
  formatPriorities: opts.formatPriorities ?? ["flv", "hls"],
45
+ quality: opts.quality,
39
46
  });
40
47
  if (!info.living) {
41
48
  throw new Error("It must be called getStream when living");
@@ -72,7 +79,7 @@ export async function getStream(opts) {
72
79
  }
73
80
  let url = expectSource.url;
74
81
  // MP协议下原画不需要添加ratio参数
75
- if (expectStream.bitRate) {
82
+ if (expectStream.bitRate && expectStream.bitRate !== -1 && !url.includes("ratio=")) {
76
83
  url = url + "&ratio=" + expectStream.bitRate;
77
84
  }
78
85
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bililive-tools/huya-recorder",
3
- "version": "1.3.2",
3
+ "version": "1.7.0",
4
4
  "description": "bililive-tools huya recorder implemention",
5
5
  "main": "./lib/index.js",
6
6
  "type": "module",
@@ -37,8 +37,8 @@
37
37
  "mitt": "^3.0.1",
38
38
  "lodash-es": "^4.17.21",
39
39
  "axios": "^1.7.8",
40
- "@bililive-tools/manager": "^1.4.1",
41
- "huya-danma-listener": "0.1.2"
40
+ "huya-danma-listener": "0.1.2",
41
+ "@bililive-tools/manager": "^1.6.1"
42
42
  },
43
43
  "devDependencies": {},
44
44
  "scripts": {