@bililive-tools/bilibili-recorder 1.0.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/lib/stream.js ADDED
@@ -0,0 +1,220 @@
1
+ import { BiliQualities, utils } from "@bililive-tools/manager";
2
+ import { getRoomInit, getRoomPlayInfo, getStatusInfoByUIDs, getRoomBaseInfo, } from "./bilibili_api.js";
3
+ import { assert } from "./utils.js";
4
+ export async function getStrictStream(roomId, options) {
5
+ const res = await getRoomPlayInfo(roomId, options);
6
+ const streamInfo = res.playurl_info.playurl.stream
7
+ .find(({ protocol_name }) => protocol_name === options.protocol_name)
8
+ ?.format.find(({ format_name }) => format_name === options.format_name)
9
+ ?.codec.find(({ codec_name }) => codec_name === options.codec_name);
10
+ assert(streamInfo, "没有找到支持的流");
11
+ const expectSource = streamInfo.url_info[0];
12
+ const url = expectSource.host + streamInfo.base_url + expectSource.extra;
13
+ return url;
14
+ }
15
+ export async function getLiveStatus(channelId) {
16
+ const roomInit = await getRoomInit(Number(channelId));
17
+ const startTime = new Date(roomInit.live_time * 1000);
18
+ return {
19
+ living: roomInit.live_status === 1 && !roomInit.encrypted,
20
+ liveId: utils.md5(`${roomInit.room_id}-${startTime?.getTime()}`),
21
+ ...roomInit,
22
+ };
23
+ }
24
+ export async function getInfo(channelId) {
25
+ const roomInit = await getRoomInit(Number(channelId));
26
+ const { [roomInit.uid]: status } = await getStatusInfoByUIDs([roomInit.uid]);
27
+ if (!status) {
28
+ // 未获取到直播间信息,可能是加密,尝试换一个接口
29
+ const data = await getRoomBaseInfo(Number(channelId));
30
+ const status = data[channelId];
31
+ const startTime = new Date(status.live_time);
32
+ return {
33
+ uid: roomInit.uid,
34
+ living: roomInit.live_status === 1 && !roomInit.encrypted,
35
+ owner: status.uname,
36
+ title: status.title,
37
+ startTime: startTime,
38
+ avatar: "",
39
+ cover: status.cover,
40
+ roomId: roomInit.room_id,
41
+ shortId: roomInit.short_id,
42
+ liveId: utils.md5(`${roomInit.room_id}-${startTime?.getTime()}`),
43
+ };
44
+ }
45
+ const startTime = new Date(status.live_time * 1000);
46
+ return {
47
+ uid: roomInit.uid,
48
+ living: roomInit.live_status === 1 && !roomInit.encrypted,
49
+ owner: status.uname,
50
+ title: status.title,
51
+ avatar: status.face,
52
+ cover: status.cover_from_user,
53
+ roomId: roomInit.room_id,
54
+ shortId: roomInit.short_id,
55
+ startTime: startTime,
56
+ liveId: utils.md5(`${roomInit.room_id}-${startTime.getTime()}`),
57
+ };
58
+ }
59
+ async function getLiveInfo(roomIdOrShortId, opts) {
60
+ const res = await getRoomPlayInfo(roomIdOrShortId, opts);
61
+ // https://github.com/FFmpeg/FFmpeg/commit/b76053d8bf322b197a9d07bd27bbdad14fd5bc15
62
+ let conditons = [
63
+ {
64
+ protocol_name: "http_stream",
65
+ format_name: "flv",
66
+ codec_name: "avc",
67
+ sort: 9,
68
+ },
69
+ {
70
+ protocol_name: "http_hls",
71
+ format_name: "fmp4",
72
+ codec_name: "avc",
73
+ sort: 8,
74
+ },
75
+ {
76
+ protocol_name: "http_hls",
77
+ format_name: "ts",
78
+ codec_name: "avc",
79
+ sort: 7,
80
+ },
81
+ {
82
+ protocol_name: "http_stream",
83
+ format_name: "flv",
84
+ codec_name: "hevc",
85
+ sort: 6,
86
+ },
87
+ {
88
+ protocol_name: "http_hls",
89
+ format_name: "fmp4",
90
+ codec_name: "hevc",
91
+ sort: 5,
92
+ },
93
+ {
94
+ protocol_name: "http_hls",
95
+ format_name: "ts",
96
+ codec_name: "hevc",
97
+ sort: 4,
98
+ },
99
+ ];
100
+ // 处理formatName
101
+ if (opts.formatName === "flv_only") {
102
+ conditons = conditons.filter((item) => item.format_name === "flv");
103
+ }
104
+ else if (opts.formatName === "hls_only") {
105
+ conditons = conditons.filter((item) => item.format_name === "ts");
106
+ }
107
+ else if (opts.formatName === "fmp4_only") {
108
+ conditons = conditons.filter((item) => item.format_name === "fmp4");
109
+ }
110
+ else if (opts.formatName === "hls") {
111
+ // hls优先,avc比hevc优先
112
+ conditons.forEach((item) => {
113
+ if (item.format_name === "ts") {
114
+ item.sort += 10;
115
+ }
116
+ });
117
+ conditons = conditons.sort((a, b) => b.sort - a.sort);
118
+ }
119
+ else if (opts.formatName === "fmp4") {
120
+ // fmp4优先,将format_name=fmp4的放在前面,avc比hevc优先
121
+ conditons.forEach((item) => {
122
+ if (item.format_name === "fmp4") {
123
+ item.sort += 10;
124
+ }
125
+ });
126
+ conditons = conditons.sort((a, b) => b.sort - a.sort);
127
+ }
128
+ // 处理codecName
129
+ if (opts.codecName === "avc_only") {
130
+ conditons = conditons.filter((item) => item.codec_name === "avc");
131
+ }
132
+ else if (opts.codecName === "hevc_only") {
133
+ conditons = conditons.filter((item) => item.codec_name === "hevc");
134
+ }
135
+ else if (opts.codecName === "hevc") {
136
+ conditons.forEach((item) => {
137
+ if (item.codec_name === "hevc") {
138
+ item.sort += 100;
139
+ }
140
+ });
141
+ conditons = conditons.sort((a, b) => b.sort - a.sort);
142
+ }
143
+ console.log("conditons", opts.codecName, conditons);
144
+ let streamInfo;
145
+ let streamOptions;
146
+ for (const condition of conditons) {
147
+ streamInfo = res.playurl_info.playurl.stream
148
+ .find(({ protocol_name }) => protocol_name === condition.protocol_name)
149
+ ?.format.find(({ format_name }) => format_name === condition.format_name)
150
+ ?.codec.find(({ codec_name }) => codec_name === condition.codec_name);
151
+ if (streamInfo) {
152
+ streamOptions = {
153
+ ...condition,
154
+ qn: streamInfo.current_qn,
155
+ };
156
+ break;
157
+ }
158
+ }
159
+ console.log("streamOptions", streamOptions, res.playurl_info.playurl.stream);
160
+ assert(streamInfo, "没有找到支持的流");
161
+ const streams = streamInfo.accept_qn.map((qn) => {
162
+ const qnDesc = res.playurl_info.playurl.g_qn_desc.find((item) => item.qn === qn);
163
+ assert(qnDesc, "Unexpected getRoomPlayInfo resp");
164
+ return qnDesc;
165
+ });
166
+ const sources = streamInfo.url_info.map((info, idx) => ({
167
+ ...info,
168
+ name: idx === 0 ? "主线" : `备线 ${idx}`,
169
+ }));
170
+ const currentStreamName = res.playurl_info.playurl.g_qn_desc.find((item) => item.qn === streamInfo.current_qn)?.desc;
171
+ assert(currentStreamName, "Unexpected getRoomPlayInfo resp");
172
+ return {
173
+ ...streamInfo,
174
+ streams,
175
+ sources,
176
+ name: currentStreamName,
177
+ streamOptions,
178
+ };
179
+ }
180
+ export async function getStream(opts) {
181
+ const roomId = Number(opts.channelId);
182
+ const roomInit = await getRoomInit(roomId);
183
+ if (roomInit.live_status !== 1) {
184
+ throw new Error("It must be called getStream when living");
185
+ }
186
+ const qn = BiliQualities.includes(opts.quality) ? opts.quality : 10000;
187
+ let liveInfo = await getLiveInfo(roomId, {
188
+ qn: qn,
189
+ cookie: opts.cookie,
190
+ formatName: opts.formatName,
191
+ codecName: opts.codecName,
192
+ });
193
+ // console.log(JSON.stringify(liveInfo, null, 2));
194
+ if (liveInfo.current_qn !== qn && opts.strictQuality) {
195
+ throw new Error("Can not get expect quality because of strictQuality");
196
+ }
197
+ if ((liveInfo?.accept_qn ?? []).length !== 0 && liveInfo.current_qn !== qn) {
198
+ // 当前流不是预期的流,需要切换。
199
+ const acceptQn = liveInfo.accept_qn[0];
200
+ liveInfo = await getLiveInfo(roomId, {
201
+ qn: acceptQn,
202
+ cookie: opts.cookie,
203
+ formatName: opts.formatName,
204
+ codecName: opts.codecName,
205
+ });
206
+ }
207
+ let expectSource = liveInfo.sources[0];
208
+ if (!expectSource) {
209
+ throw new Error("Can not get expect source");
210
+ }
211
+ return {
212
+ ...liveInfo,
213
+ currentStream: {
214
+ name: liveInfo.name,
215
+ source: expectSource.name,
216
+ url: expectSource.host + liveInfo.base_url + expectSource.extra,
217
+ },
218
+ };
219
+ }
220
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}
package/lib/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/lib/test.js ADDED
@@ -0,0 +1,14 @@
1
+ // execute in shell `ts-node src/test.ts` to run test
2
+ // TODO: add to scripts
3
+ import { createRecorderManager } from "@bililive-tools/manager";
4
+ import { provider } from "./index.js";
5
+ const manager = createRecorderManager({ providers: [provider] });
6
+ manager.addRecorder({
7
+ providerId: provider.id,
8
+ channelId: "7734200",
9
+ quality: "low",
10
+ streamPriorities: [],
11
+ sourcePriorities: [],
12
+ });
13
+ manager.startCheckLoop();
14
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,qDAAqD;AACrD,uBAAuB;AACvB,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,MAAM,OAAO,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AACjE,OAAO,CAAC,WAAW,CAAC;IAClB,UAAU,EAAE,QAAQ,CAAC,EAAE;IACvB,SAAS,EAAE,SAAS;IACpB,OAAO,EAAE,KAAK;IACd,gBAAgB,EAAE,EAAE;IACpB,gBAAgB,EAAE,EAAE;CACrB,CAAC,CAAC;AACH,OAAO,CAAC,cAAc,EAAE,CAAC"}
package/lib/utils.d.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * 从数组中按照特定算法提取一些值(允许同个索引重复提取)。
3
+ * 算法的行为类似 flex 的 space-between。
4
+ *
5
+ * examples:
6
+ * ```
7
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 1))
8
+ * // [1]
9
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 3))
10
+ * // [1, 4, 7]
11
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 4))
12
+ * // [1, 3, 5, 7]
13
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 11))
14
+ * // [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7]
15
+ * ```
16
+ */
17
+ export declare function getValuesFromArrayLikeFlexSpaceBetween<T>(array: T[], columnCount: number): T[];
18
+ export declare function ensureFolderExist(fileOrFolderPath: string): void;
19
+ export declare function assert(assertion: unknown, msg?: string): asserts assertion;
20
+ export declare function assertStringType(data: unknown, msg?: string): asserts data is string;
21
+ export declare function assertNumberType(data: unknown, msg?: string): asserts data is number;
22
+ export declare function assertObjectType(data: unknown, msg?: string): asserts data is object;
23
+ export declare function createInvalidStreamChecker(): (ffmpegLogLine: string) => boolean;
package/lib/utils.js ADDED
@@ -0,0 +1,87 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { range } from "lodash-es";
4
+ /**
5
+ * 从数组中按照特定算法提取一些值(允许同个索引重复提取)。
6
+ * 算法的行为类似 flex 的 space-between。
7
+ *
8
+ * examples:
9
+ * ```
10
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 1))
11
+ * // [1]
12
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 3))
13
+ * // [1, 4, 7]
14
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 4))
15
+ * // [1, 3, 5, 7]
16
+ * console.log(getValuesFromArrayLikeFlexSpaceBetween([1, 2, 3, 4, 5, 6, 7], 11))
17
+ * // [1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7]
18
+ * ```
19
+ */
20
+ export function getValuesFromArrayLikeFlexSpaceBetween(array, columnCount) {
21
+ if (columnCount < 1)
22
+ return [];
23
+ if (columnCount === 1)
24
+ return [array[0]];
25
+ const spacingCount = columnCount - 1;
26
+ const spacingLength = array.length / spacingCount;
27
+ const columns = range(1, columnCount + 1);
28
+ const columnValues = columns.map((column, idx, columns) => {
29
+ // 首个和最后的列是特殊的,因为它们不在范围内,而是在两端
30
+ if (idx === 0) {
31
+ return array[0];
32
+ }
33
+ else if (idx === columns.length - 1) {
34
+ return array[array.length - 1];
35
+ }
36
+ const beforeSpacingCount = column - 1;
37
+ const colPos = beforeSpacingCount * spacingLength;
38
+ return array[Math.floor(colPos)];
39
+ });
40
+ return columnValues;
41
+ }
42
+ export function ensureFolderExist(fileOrFolderPath) {
43
+ const folder = path.dirname(fileOrFolderPath);
44
+ if (!fs.existsSync(folder)) {
45
+ fs.mkdirSync(folder, { recursive: true });
46
+ }
47
+ }
48
+ export function assert(assertion, msg) {
49
+ if (!assertion) {
50
+ throw new Error(msg);
51
+ }
52
+ }
53
+ export function assertStringType(data, msg) {
54
+ assert(typeof data === "string", msg);
55
+ }
56
+ export function assertNumberType(data, msg) {
57
+ assert(typeof data === "number", msg);
58
+ }
59
+ export function assertObjectType(data, msg) {
60
+ assert(typeof data === "object", msg);
61
+ }
62
+ export function createInvalidStreamChecker() {
63
+ let prevFrame = 0;
64
+ let frameUnchangedCount = 0;
65
+ return (ffmpegLogLine) => {
66
+ const streamInfo = ffmpegLogLine.match(/frame=\s*(\d+) fps=.*? q=.*? size=.*? time=.*? bitrate=.*? speed=.*?/);
67
+ if (streamInfo != null) {
68
+ const [, frameText] = streamInfo;
69
+ const frame = Number(frameText);
70
+ if (frame === prevFrame) {
71
+ if (++frameUnchangedCount >= 10) {
72
+ return true;
73
+ }
74
+ }
75
+ else {
76
+ prevFrame = frame;
77
+ frameUnchangedCount = 0;
78
+ }
79
+ return false;
80
+ }
81
+ // if (ffmpegLogLine.includes("HTTP error 404 Not Found")) {
82
+ // return true;
83
+ // }
84
+ return false;
85
+ };
86
+ }
87
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@bililive-tools/bilibili-recorder",
3
+ "version": "1.0.0",
4
+ "description": "LAR bilibili recorder implemention",
5
+ "main": "./lib/index.js",
6
+ "type": "module",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./src/index.ts",
13
+ "development": "./src/index.ts",
14
+ "default": "./lib/index.js"
15
+ },
16
+ "./*.js": {
17
+ "types": "./src/*.ts",
18
+ "development": "./src/*.ts",
19
+ "default": "./lib/*.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "lib"
24
+ ],
25
+ "repository": "https://github.com/renmu123/biliLive-tools/tree/master/packages/BilibiliRecorder",
26
+ "author": "renmu123",
27
+ "license": "LGPL",
28
+ "dependencies": {
29
+ "blive-message-listener": "^0.5.0",
30
+ "mitt": "^3.0.1",
31
+ "tiny-bilibili-ws": "^1.0.1",
32
+ "@bililive-tools/manager": "1.0.0"
33
+ },
34
+ "scripts": {
35
+ "build": "tsc",
36
+ "watch": "tsc -w"
37
+ }
38
+ }