@bililive-tools/douyin-recorder 1.17.0 → 1.17.2
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/douyin_api.d.ts +1 -0
- package/lib/douyin_api.js +55 -41
- package/lib/index.js +3 -0
- package/lib/stream.d.ts +3 -0
- package/lib/stream.js +11 -2
- package/lib/types.d.ts +1 -0
- package/package.json +1 -1
package/lib/douyin_api.d.ts
CHANGED
package/lib/douyin_api.js
CHANGED
|
@@ -159,6 +159,7 @@ async function getRoomInfoByUserWeb(secUserId, opts = {}) {
|
|
|
159
159
|
if (!res.data.includes("直播中")) {
|
|
160
160
|
return {
|
|
161
161
|
living: false,
|
|
162
|
+
isLiveRadio: false,
|
|
162
163
|
nickname: "",
|
|
163
164
|
sec_uid: "",
|
|
164
165
|
avatar: "",
|
|
@@ -177,19 +178,38 @@ async function getRoomInfoByUserWeb(secUserId, opts = {}) {
|
|
|
177
178
|
.replace(/\\"/g, '"')
|
|
178
179
|
.replace(/\\"/g, '"')
|
|
179
180
|
.replace(/"\$\w+"/g, "null");
|
|
180
|
-
// const roomRegex = /(\{\\"common\\":.*?)"\]\)/;
|
|
181
|
-
// const roomMatch = res.data.match(roomRegex);
|
|
182
|
-
// if (!roomMatch) {
|
|
183
|
-
// throw new Error("No room match found in HTML");
|
|
184
|
-
// }
|
|
185
|
-
// let roomJsonStr = roomMatch[1];
|
|
186
|
-
// roomJsonStr = roomJsonStr
|
|
187
|
-
// .replace(/\\"/g, '"')
|
|
188
|
-
// .replace(/\\"/g, '"')
|
|
189
|
-
// .replace(/"\$\w+"/g, "null");
|
|
190
181
|
try {
|
|
191
|
-
// console.log(userJsonStr);
|
|
192
182
|
const userData = JSON.parse(userJsonStr);
|
|
183
|
+
const roomData = userData?.user?.user?.roomData;
|
|
184
|
+
const streamUrl = roomData?.stream_url;
|
|
185
|
+
let liveCoreSdkData = null;
|
|
186
|
+
if (streamUrl) {
|
|
187
|
+
liveCoreSdkData = { live_core_sdk_data: streamUrl.live_core_sdk_data };
|
|
188
|
+
if (liveCoreSdkData?.live_core_sdk_data?.pull_data) {
|
|
189
|
+
const flvPullUrl = streamUrl.flv_pull_url;
|
|
190
|
+
let streamData = {};
|
|
191
|
+
for (const quality of [{ key: "or4", desc: "原画" }, ...qualityList]) {
|
|
192
|
+
const flvUrls = Object.values(flvPullUrl);
|
|
193
|
+
if (flvUrls.some((url) => url.includes(`${quality.key}`))) {
|
|
194
|
+
const url = flvUrls.find((url) => url.includes(`${quality.key}`));
|
|
195
|
+
const convertedQuality = quality.key === "or4" ? "origin" : quality.key;
|
|
196
|
+
streamData[convertedQuality] = {
|
|
197
|
+
// @ts-ignore
|
|
198
|
+
main: {
|
|
199
|
+
flv: url,
|
|
200
|
+
hls: "",
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
liveCoreSdkData.live_core_sdk_data.pull_data.stream_data = streamData;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
liveCoreSdkData = null;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// console.log("pppp", JSON.stringify(liveCoreSdkData, null, 2));
|
|
212
|
+
// console.log(JSON.stringify(streamUrl, null, 2));
|
|
193
213
|
// console.log(JSON.stringify(userData, null, 2));
|
|
194
214
|
// const roomData = JSON.parse(roomJsonStr);
|
|
195
215
|
// console.log(roomData);
|
|
@@ -197,15 +217,16 @@ async function getRoomInfoByUserWeb(secUserId, opts = {}) {
|
|
|
197
217
|
// const streamData = data.state.streamStore.streamData;
|
|
198
218
|
return {
|
|
199
219
|
living: userData?.user?.user?.roomData?.status === 2,
|
|
220
|
+
isLiveRadio: roomData?.live_type_audio ?? false,
|
|
200
221
|
nickname: userData?.user?.user?.nickname ?? "",
|
|
201
222
|
sec_uid: userData?.user?.user?.secUid ?? "",
|
|
202
223
|
avatar: userData?.user?.user?.avatar ?? "",
|
|
203
|
-
api: "
|
|
224
|
+
api: "userHTML",
|
|
204
225
|
room: {
|
|
205
226
|
title: "",
|
|
206
227
|
cover: "",
|
|
207
228
|
id_str: userData?.user?.user?.roomIdStr,
|
|
208
|
-
stream_url:
|
|
229
|
+
stream_url: liveCoreSdkData,
|
|
209
230
|
},
|
|
210
231
|
};
|
|
211
232
|
}
|
|
@@ -250,8 +271,10 @@ async function getRoomInfoByHtml(webRoomId, opts = {}) {
|
|
|
250
271
|
const data = JSON.parse(jsonStr);
|
|
251
272
|
const roomInfo = data.state.roomStore.roomInfo;
|
|
252
273
|
const streamData = data.state.streamStore.streamData;
|
|
274
|
+
const isLiveRadio = roomInfo.enter_mode == 1;
|
|
253
275
|
return {
|
|
254
|
-
living: roomInfo?.room?.status === 2,
|
|
276
|
+
living: roomInfo?.room?.status === 2 || isLiveRadio,
|
|
277
|
+
isLiveRadio: isLiveRadio,
|
|
255
278
|
nickname: roomInfo?.anchor?.nickname ?? "",
|
|
256
279
|
sec_uid: roomInfo?.anchor?.sec_uid ?? "",
|
|
257
280
|
avatar: roomInfo?.anchor?.avatar_thumb?.url_list?.[0] ?? "",
|
|
@@ -260,15 +283,17 @@ async function getRoomInfoByHtml(webRoomId, opts = {}) {
|
|
|
260
283
|
title: roomInfo?.room?.title ?? "",
|
|
261
284
|
cover: roomInfo?.room?.cover?.url_list?.[0] ?? "",
|
|
262
285
|
id_str: roomInfo?.room?.id_str ?? "",
|
|
263
|
-
stream_url:
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
286
|
+
stream_url: roomInfo?.room?.stream_url?.pull_datas
|
|
287
|
+
? {
|
|
288
|
+
pull_datas: roomInfo?.room?.stream_url?.pull_datas,
|
|
289
|
+
live_core_sdk_data: {
|
|
290
|
+
pull_data: {
|
|
291
|
+
options: { qualities: streamData.H264_streamData?.options?.qualities ?? [] },
|
|
292
|
+
stream_data: streamData.H264_streamData?.stream ?? {},
|
|
293
|
+
},
|
|
269
294
|
},
|
|
270
|
-
}
|
|
271
|
-
|
|
295
|
+
}
|
|
296
|
+
: null,
|
|
272
297
|
},
|
|
273
298
|
};
|
|
274
299
|
}
|
|
@@ -316,6 +341,7 @@ async function getRoomInfoByWeb(webRoomId, opts = {}) {
|
|
|
316
341
|
// 直播已结束
|
|
317
342
|
return {
|
|
318
343
|
living: false,
|
|
344
|
+
isLiveRadio: false,
|
|
319
345
|
nickname: "",
|
|
320
346
|
sec_uid: "",
|
|
321
347
|
avatar: "",
|
|
@@ -332,7 +358,8 @@ async function getRoomInfoByWeb(webRoomId, opts = {}) {
|
|
|
332
358
|
const data = res.data.data;
|
|
333
359
|
const room = data?.data?.[0];
|
|
334
360
|
return {
|
|
335
|
-
living: data?.room_status === 0,
|
|
361
|
+
living: data?.room_status === 0 || data?.room_status === 1,
|
|
362
|
+
isLiveRadio: data?.room_status === 1,
|
|
336
363
|
nickname: data?.user?.nickname ?? "",
|
|
337
364
|
avatar: data?.user?.avatar_thumb?.url_list?.[0] ?? "",
|
|
338
365
|
sec_uid: data?.user?.sec_uid ?? "",
|
|
@@ -371,6 +398,7 @@ async function getRoomInfoByMobile(secUserId, opts = {}) {
|
|
|
371
398
|
const room = res?.data?.data?.room;
|
|
372
399
|
return {
|
|
373
400
|
living: room?.status === 2,
|
|
401
|
+
isLiveRadio: room?.live_type_audio ?? false,
|
|
374
402
|
nickname: room?.owner?.nickname,
|
|
375
403
|
sec_uid: room?.owner?.sec_uid,
|
|
376
404
|
avatar: room?.owner?.avatar_thumb?.url_list?.[0],
|
|
@@ -408,11 +436,12 @@ export async function getRoomInfo(webRoomId, opts = {}) {
|
|
|
408
436
|
else {
|
|
409
437
|
data = await getRoomInfoByWeb(webRoomId, opts);
|
|
410
438
|
}
|
|
411
|
-
// console.log(JSON.stringify(data, null, 2));
|
|
412
439
|
const room = data.room;
|
|
413
|
-
|
|
440
|
+
assert(room, `No room data, id ${webRoomId}`);
|
|
441
|
+
if (!room?.stream_url) {
|
|
414
442
|
return {
|
|
415
443
|
living: data.living,
|
|
444
|
+
isLiveRadio: data.isLiveRadio,
|
|
416
445
|
roomId: webRoomId,
|
|
417
446
|
owner: data.nickname,
|
|
418
447
|
title: room?.title ?? data.nickname,
|
|
@@ -425,22 +454,6 @@ export async function getRoomInfo(webRoomId, opts = {}) {
|
|
|
425
454
|
api: data.api,
|
|
426
455
|
};
|
|
427
456
|
}
|
|
428
|
-
assert(room, `No room data, id ${webRoomId}`);
|
|
429
|
-
if (room?.stream_url == null) {
|
|
430
|
-
return {
|
|
431
|
-
living: false,
|
|
432
|
-
roomId: webRoomId,
|
|
433
|
-
owner: data.nickname,
|
|
434
|
-
title: room?.title ?? data.nickname,
|
|
435
|
-
streams: [],
|
|
436
|
-
sources: [],
|
|
437
|
-
avatar: data.avatar,
|
|
438
|
-
cover: room.cover,
|
|
439
|
-
liveId: room.id_str,
|
|
440
|
-
uid: data.sec_uid,
|
|
441
|
-
api: data.api,
|
|
442
|
-
};
|
|
443
|
-
}
|
|
444
457
|
let qualities = [];
|
|
445
458
|
let stream_data = "";
|
|
446
459
|
if (opts.doubleScreen && !isEmpty(room.stream_url.pull_datas)) {
|
|
@@ -509,6 +522,7 @@ export async function getRoomInfo(webRoomId, opts = {}) {
|
|
|
509
522
|
// console.log(JSON.stringify(sources, null, 2), qualities);
|
|
510
523
|
return {
|
|
511
524
|
living: data.living,
|
|
525
|
+
isLiveRadio: data.isLiveRadio,
|
|
512
526
|
roomId: webRoomId,
|
|
513
527
|
owner: data.nickname,
|
|
514
528
|
title: room.title,
|
package/lib/index.js
CHANGED
|
@@ -77,6 +77,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
77
77
|
return this.recordHandle;
|
|
78
78
|
}
|
|
79
79
|
// 获取直播间信息
|
|
80
|
+
let isLiveRadio = false;
|
|
80
81
|
try {
|
|
81
82
|
const liveInfo = await getInfo(this.channelId, {
|
|
82
83
|
auth: this.auth,
|
|
@@ -84,6 +85,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
84
85
|
uid: this.uid,
|
|
85
86
|
});
|
|
86
87
|
this.liveInfo = liveInfo;
|
|
88
|
+
isLiveRadio = liveInfo.isLiveRadio;
|
|
87
89
|
this.emit("stateChange", { state: "idle" });
|
|
88
90
|
}
|
|
89
91
|
catch (error) {
|
|
@@ -121,6 +123,7 @@ const checkLiveStatusAndRecord = async function ({ getSavePath, banLiveId, isMan
|
|
|
121
123
|
doubleScreen: this.doubleScreen,
|
|
122
124
|
api: this.api,
|
|
123
125
|
uid: this.uid,
|
|
126
|
+
isLiveRadio: isLiveRadio,
|
|
124
127
|
});
|
|
125
128
|
this.liveInfo.owner = res.owner;
|
|
126
129
|
this.liveInfo.title = res.title;
|
package/lib/stream.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export declare function getInfo(channelId: string, opts?: {
|
|
|
6
6
|
uid?: string | number;
|
|
7
7
|
}): Promise<{
|
|
8
8
|
living: boolean;
|
|
9
|
+
isLiveRadio: boolean;
|
|
9
10
|
owner: string;
|
|
10
11
|
title: string;
|
|
11
12
|
roomId: string;
|
|
@@ -26,6 +27,7 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality" |
|
|
|
26
27
|
doubleScreen?: boolean;
|
|
27
28
|
api?: APIType;
|
|
28
29
|
uid?: string | number;
|
|
30
|
+
isLiveRadio?: boolean;
|
|
29
31
|
}): Promise<{
|
|
30
32
|
currentStream: {
|
|
31
33
|
name: string;
|
|
@@ -34,6 +36,7 @@ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality" |
|
|
|
34
36
|
onlyAudio: boolean;
|
|
35
37
|
};
|
|
36
38
|
living: boolean;
|
|
39
|
+
isLiveRadio?: boolean;
|
|
37
40
|
roomId: string;
|
|
38
41
|
owner: string;
|
|
39
42
|
title: string;
|
package/lib/stream.js
CHANGED
|
@@ -15,6 +15,7 @@ export async function getInfo(channelId, opts) {
|
|
|
15
15
|
const startTime = new Date();
|
|
16
16
|
return {
|
|
17
17
|
living: info.living,
|
|
18
|
+
isLiveRadio: info.isLiveRadio,
|
|
18
19
|
owner: info.owner,
|
|
19
20
|
title: info.title,
|
|
20
21
|
roomId: info.roomId,
|
|
@@ -30,12 +31,20 @@ export async function getInfo(channelId, opts) {
|
|
|
30
31
|
}
|
|
31
32
|
export async function getStream(opts) {
|
|
32
33
|
let api = opts.api ?? "web";
|
|
33
|
-
if (api === "userHTML") {
|
|
34
|
+
if (api === "userHTML" || api === "webHTML") {
|
|
34
35
|
// userHTML 接口只能用于状态检测
|
|
35
36
|
api = "web";
|
|
36
37
|
}
|
|
37
38
|
else if (api === "random") {
|
|
38
|
-
api = selectRandomAPI(["userHTML"]);
|
|
39
|
+
api = selectRandomAPI(["userHTML", "webHTML"]);
|
|
40
|
+
}
|
|
41
|
+
if (opts.isLiveRadio) {
|
|
42
|
+
// 直播电台可能需要特殊处理,优先使用 mobile 接口,某些直播间userHTML不会有数据,mobile可能更加合适
|
|
43
|
+
api = "mobile";
|
|
44
|
+
// 如果用户指定了userHTML接口,那么优先使用userHTML接口获取流信息
|
|
45
|
+
if (opts.api === "userHTML") {
|
|
46
|
+
api = "userHTML";
|
|
47
|
+
}
|
|
39
48
|
}
|
|
40
49
|
const info = await getRoomInfo(opts.channelId, {
|
|
41
50
|
doubleScreen: opts.doubleScreen ?? true,
|
package/lib/types.d.ts
CHANGED