@bililive-tools/douyu-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.
@@ -0,0 +1,106 @@
1
+ /**
2
+ * 本来打算直接用 douyudm 这个库,但有比较多的地方不符合我的需求,
3
+ * 所以参考着这个库以及斗鱼的代码进行了重新实现。
4
+ * reference: https://github.com/flxxyz/douyudm
5
+ */
6
+ import WebSocket from "ws";
7
+ import mitt from "mitt";
8
+ import { BufferCoder } from "./buffer_coder.js";
9
+ import { STT } from "./stt.js";
10
+ export function createDYClient(channelId, opts = {}) {
11
+ let ws = null;
12
+ let maxRetry = 5;
13
+ let coder = new BufferCoder();
14
+ let heartbeatTimer = null;
15
+ const send = (message) => ws?.send(coder.encode(STT.serialize(message)));
16
+ const sendLogin = () => send({ type: "loginreq", roomid: channelId });
17
+ const sendJoinGroup = () => send({ type: "joingroup", rid: channelId, gid: -9999 });
18
+ const sendHeartbeat = () => send({ type: "mrkl" });
19
+ const sendLogout = () => send({ type: "logout" });
20
+ const onOpen = () => {
21
+ sendLogin();
22
+ sendJoinGroup();
23
+ heartbeatTimer = setInterval(sendHeartbeat, 45e3);
24
+ };
25
+ const onClose = () => {
26
+ sendLogout();
27
+ if (heartbeatTimer) {
28
+ // @ts-ignore
29
+ clearInterval(heartbeatTimer);
30
+ heartbeatTimer = null;
31
+ }
32
+ };
33
+ const onError = (err) => {
34
+ if (maxRetry > 0) {
35
+ maxRetry -= 1;
36
+ stop();
37
+ setTimeout(() => {
38
+ start();
39
+ }, 3e3);
40
+ }
41
+ else {
42
+ client.emit("error", err);
43
+ client.emit("error", new Error("重连次数过多,停止重连"));
44
+ }
45
+ };
46
+ const onMessage = (message) => {
47
+ if (typeof message != "object" || message == null || !("type" in message)) {
48
+ console.warn("Unexpected message format", { message });
49
+ return;
50
+ }
51
+ client.emit("message",
52
+ // TODO: 不太好验证 schema,先强制转了
53
+ message);
54
+ };
55
+ const start = () => {
56
+ if (ws != null)
57
+ return;
58
+ ws = new WebSocket(getRandomPortWSURL());
59
+ coder = new BufferCoder();
60
+ ws.binaryType = "arraybuffer";
61
+ ws.on("open", onOpen);
62
+ ws.on("error", onError);
63
+ ws.on("close", onClose);
64
+ ws.on("message", (data) => {
65
+ if (!(data instanceof ArrayBuffer)) {
66
+ throw new Error("Do not meet the types of ws.binaryType expected");
67
+ }
68
+ coder.decode(data, (messageText) => {
69
+ try {
70
+ const message = STT.deserialize(messageText);
71
+ // @ts-ignore
72
+ if (message?.type === "comm_chatmsg" && message?.chatmsg) {
73
+ // @ts-ignore
74
+ message.chatmsg = STT.deserialize(message?.chatmsg);
75
+ }
76
+ onMessage(message);
77
+ }
78
+ catch (error) {
79
+ client.emit("error", error);
80
+ }
81
+ });
82
+ });
83
+ };
84
+ const stop = () => {
85
+ if (ws == null)
86
+ return;
87
+ onClose();
88
+ ws = null;
89
+ };
90
+ if (!opts.notAutoStart) {
91
+ start();
92
+ }
93
+ const client = {
94
+ // @ts-ignore
95
+ ...mitt(),
96
+ start,
97
+ stop,
98
+ send,
99
+ };
100
+ return client;
101
+ }
102
+ function getRandomPortWSURL() {
103
+ const port = 8500 + ((min, max) => Math.floor(Math.random() * (max - min + 1) + min))(1, 6);
104
+ return `wss://danmuproxy.douyu.com:${port}/`;
105
+ }
106
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/dy_client/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export declare namespace STT {
2
+ function escape(v: string): string;
3
+ function unescape(v: string): string;
4
+ function serialize(obj: unknown): string;
5
+ function deserialize(raw: string): unknown;
6
+ }
@@ -0,0 +1,46 @@
1
+ export var STT;
2
+ (function (STT) {
3
+ function escape(v) {
4
+ return v.toString().replace(/@/g, "@A").replace(/\//g, "@S");
5
+ }
6
+ STT.escape = escape;
7
+ function unescape(v) {
8
+ return v.toString().replace(/@S/g, "/").replace(/@A/g, "@");
9
+ }
10
+ STT.unescape = unescape;
11
+ function serialize(obj) {
12
+ if (obj == null)
13
+ throw new Error("Cant serialize null value");
14
+ if (Array.isArray(obj)) {
15
+ return obj.map((v) => STT.serialize(v)).join("");
16
+ }
17
+ if (typeof obj === "object") {
18
+ return Object.entries(obj)
19
+ .map(([k, v]) => `${k}@=${STT.serialize(v)}`)
20
+ .join("");
21
+ }
22
+ return STT.escape(obj.toString()) + "/";
23
+ }
24
+ STT.serialize = serialize;
25
+ function deserialize(raw) {
26
+ if (raw.includes("//")) {
27
+ return raw
28
+ .split("//")
29
+ .filter((e) => e !== "")
30
+ .map((item) => STT.deserialize(item));
31
+ }
32
+ if (raw.includes("@=")) {
33
+ return raw
34
+ .split("/")
35
+ .filter((part) => part !== "")
36
+ .reduce((obj, part) => {
37
+ const [key, val] = part.split("@=");
38
+ obj[key] = val ? STT.deserialize(val) : "";
39
+ return obj;
40
+ }, {});
41
+ }
42
+ return STT.unescape(raw);
43
+ }
44
+ STT.deserialize = deserialize;
45
+ })(STT || (STT = {}));
46
+ //# sourceMappingURL=stt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stt.js","sourceRoot":"","sources":["../../src/dy_client/stt.ts"],"names":[],"mappings":"AAAA,MAAM,KAAW,GAAG,CAiDnB;AAjDD,WAAiB,GAAG;IAClB,SAAgB,MAAM,CAAC,CAAS;QAC9B,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC/D,CAAC;IAFe,UAAM,SAErB,CAAA;IAED,SAAgB,QAAQ,CAAC,CAAS;QAChC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9D,CAAC;IAFe,YAAQ,WAEvB,CAAA;IAED,SAAgB,SAAS,CAAC,GAAY;QACpC,IAAI,GAAG,IAAI,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,2BAA2B,CAAC,CAAC;QAE9D,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACnD,CAAC;QAED,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;YAC5B,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;iBACvB,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;iBAC5C,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,CAAC;QAED,OAAO,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,GAAG,GAAG,CAAC;IAC1C,CAAC;IAde,aAAS,YAcxB,CAAA;IAED,SAAgB,WAAW,CAAC,GAAW;QACrC,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG;iBACP,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,EAAE,CAAC;iBACvB,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1C,CAAC;QAED,IAAI,GAAG,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG;iBACP,KAAK,CAAC,GAAG,CAAC;iBACV,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC;iBAC7B,MAAM,CACL,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;gBACZ,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBACpC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3C,OAAO,GAAG,CAAC;YACb,CAAC,EACD,EAA6B,CAC9B,CAAC;QACN,CAAC;QAED,OAAO,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAvBe,eAAW,cAuB1B,CAAA;AACH,CAAC,EAjDgB,GAAG,KAAH,GAAG,QAiDnB"}
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ import { RecorderProvider } from "@bililive-tools/manager";
2
+ export declare const provider: RecorderProvider<Record<string, unknown>>;
package/lib/index.js ADDED
@@ -0,0 +1,333 @@
1
+ import mitt from "mitt";
2
+ import { createFFMPEGBuilder, defaultFromJSON, defaultToJSON, genRecorderUUID, genRecordUUID, StreamManager, utils, } from "@bililive-tools/manager";
3
+ import { getInfo, getStream } from "./stream.js";
4
+ import { getRoomInfo } from "./dy_api.js";
5
+ import { assert, ensureFolderExist } from "./utils.js";
6
+ import { createDYClient } from "./dy_client/index.js";
7
+ import { giftMap, colorTab } from "./danma.js";
8
+ import { requester } from "./requester.js";
9
+ function createRecorder(opts) {
10
+ // 内部实现时,应该只有 proxy 包裹的那一层会使用这个 recorder 标识符,不应该有直接通过
11
+ // 此标志来操作这个对象的地方,不然会跳过 proxy 的拦截。
12
+ const recorder = {
13
+ id: opts.id ?? genRecorderUUID(),
14
+ extra: opts.extra ?? {},
15
+ // @ts-ignore
16
+ ...mitt(),
17
+ ...opts,
18
+ availableStreams: [],
19
+ availableSources: [],
20
+ qualityMaxRetry: opts.qualityRetry ?? 0,
21
+ qualityRetry: opts.qualityRetry ?? 0,
22
+ state: "idle",
23
+ getChannelURL() {
24
+ return `https://www.douyu.com/${this.channelId}`;
25
+ },
26
+ checkLiveStatusAndRecord: utils.singleton(checkLiveStatusAndRecord),
27
+ toJSON() {
28
+ return defaultToJSON(provider, this);
29
+ },
30
+ async getLiveInfo() {
31
+ const channelId = this.channelId;
32
+ const info = await getInfo(channelId);
33
+ return {
34
+ channelId,
35
+ ...info,
36
+ };
37
+ },
38
+ async getStream() {
39
+ const res = await getStream({
40
+ channelId: this.channelId,
41
+ quality: this.quality,
42
+ });
43
+ return res.currentStream;
44
+ },
45
+ };
46
+ const recorderWithSupportUpdatedEvent = new Proxy(recorder, {
47
+ set(obj, prop, value) {
48
+ Reflect.set(obj, prop, value);
49
+ if (typeof prop === "string") {
50
+ obj.emit("Updated", [prop]);
51
+ }
52
+ return true;
53
+ },
54
+ });
55
+ return recorderWithSupportUpdatedEvent;
56
+ }
57
+ const ffmpegOutputOptions = [
58
+ "-c",
59
+ "copy",
60
+ "-movflags",
61
+ "faststart+frag_keyframe+empty_moov",
62
+ "-min_frag_duration",
63
+ "60000000",
64
+ ];
65
+ const checkLiveStatusAndRecord = async function ({ getSavePath, qualityRetry, banLiveId, }) {
66
+ if (this.recordHandle != null)
67
+ return this.recordHandle;
68
+ const liveInfo = await getInfo(this.channelId);
69
+ this.liveInfo = liveInfo;
70
+ const { living, owner, title, cover } = liveInfo;
71
+ if (liveInfo.liveId === banLiveId) {
72
+ this.tempStopIntervalCheck = true;
73
+ }
74
+ else {
75
+ this.tempStopIntervalCheck = false;
76
+ }
77
+ if (this.tempStopIntervalCheck)
78
+ return null;
79
+ if (!living)
80
+ return null;
81
+ this.state = "recording";
82
+ let res;
83
+ // TODO: 先不做什么错误处理,就简单包一下预期上会有错误的地方
84
+ try {
85
+ let strictQuality = !!this.qualityRetry;
86
+ if (qualityRetry !== undefined) {
87
+ strictQuality = !!qualityRetry;
88
+ }
89
+ res = await getStream({
90
+ channelId: this.channelId,
91
+ quality: this.quality,
92
+ strictQuality,
93
+ });
94
+ }
95
+ catch (err) {
96
+ this.state = "idle";
97
+ this.qualityRetry -= 1;
98
+ throw err;
99
+ }
100
+ const { currentStream: stream, sources: availableSources, streams: availableStreams } = res;
101
+ this.availableStreams = availableStreams.map((s) => s.name);
102
+ this.availableSources = availableSources.map((s) => s.name);
103
+ this.usedStream = stream.name;
104
+ this.usedSource = stream.source;
105
+ // TODO: emit update event
106
+ const savePath = getSavePath({ owner, title });
107
+ const hasSegment = !!this.segment;
108
+ const streamManager = new StreamManager(this, getSavePath, owner, title, savePath, hasSegment);
109
+ try {
110
+ ensureFolderExist(savePath);
111
+ }
112
+ catch (err) {
113
+ this.state = "idle";
114
+ throw err;
115
+ }
116
+ const handleVideoCreated = async ({ filename }) => {
117
+ const extraDataController = streamManager?.getExtraDataController();
118
+ extraDataController?.setMeta({
119
+ room_id: this.channelId,
120
+ platform: provider?.id,
121
+ liveStartTimestamp: liveInfo.startTime?.getTime(),
122
+ });
123
+ if (this.saveCover) {
124
+ const coverPath = utils.replaceExtName(filename, ".jpg");
125
+ utils.downloadImage(cover, coverPath);
126
+ }
127
+ };
128
+ this.on("videoFileCreated", handleVideoCreated);
129
+ const client = createDYClient(Number(this.channelId), {
130
+ notAutoStart: true,
131
+ });
132
+ client.on("message", (msg) => {
133
+ const extraDataController = streamManager.getExtraDataController();
134
+ if (!extraDataController)
135
+ return;
136
+ switch (msg.type) {
137
+ case "chatmsg": {
138
+ const comment = {
139
+ type: "comment",
140
+ timestamp: Date.now(),
141
+ text: msg.txt,
142
+ color: colorTab[msg.col] ?? "#ffffff",
143
+ sender: {
144
+ uid: msg.uid,
145
+ name: msg.nn,
146
+ avatar: msg.ic,
147
+ extra: {
148
+ level: msg.level,
149
+ },
150
+ },
151
+ };
152
+ this.emit("Message", comment);
153
+ extraDataController.addMessage(comment);
154
+ break;
155
+ }
156
+ case "dgb": {
157
+ if (this.saveGiftDanma === false)
158
+ return;
159
+ const gift = {
160
+ type: "give_gift",
161
+ timestamp: Date.now(),
162
+ name: giftMap[msg.gfid]?.name ?? msg.gfn,
163
+ price: (giftMap[msg.gfid]?.pc ?? 0) / 100,
164
+ count: Number(msg.gfcnt),
165
+ color: "#ffffff",
166
+ sender: {
167
+ uid: msg.uid,
168
+ name: msg.nn,
169
+ avatar: msg.ic,
170
+ extra: {
171
+ level: msg.level,
172
+ },
173
+ },
174
+ extra: {
175
+ hits: Number(msg.hits),
176
+ },
177
+ };
178
+ this.emit("Message", gift);
179
+ extraDataController.addMessage(gift);
180
+ break;
181
+ // TODO: 还有一些其他礼物相关的 msg 要处理,目前先简单点只处理 dgb
182
+ }
183
+ case "comm_chatmsg": {
184
+ if (this.saveSCDanma === false)
185
+ return;
186
+ switch (msg.btype) {
187
+ case "voiceDanmu": {
188
+ const comment = {
189
+ type: "super_chat",
190
+ timestamp: Date.now(),
191
+ text: msg?.chatmsg?.txt,
192
+ price: Number(msg.cprice) / 100,
193
+ sender: {
194
+ uid: msg.uid,
195
+ name: msg?.chatmsg?.nn,
196
+ avatar: msg?.chatmsg?.ic,
197
+ extra: {
198
+ level: msg?.chatmsg?.level,
199
+ },
200
+ },
201
+ };
202
+ this.emit("Message", comment);
203
+ extraDataController.addMessage(comment);
204
+ break;
205
+ }
206
+ }
207
+ break;
208
+ }
209
+ }
210
+ });
211
+ client.on("error", (err) => {
212
+ this.emit("DebugLog", { type: "common", text: String(err) });
213
+ });
214
+ // console.log("this.disableProvideCommentsWhenRecording", this.disableProvideCommentsWhenRecording);
215
+ if (!this.disableProvideCommentsWhenRecording) {
216
+ client.start();
217
+ }
218
+ let isEnded = false;
219
+ const onEnd = async (...args) => {
220
+ if (isEnded)
221
+ return;
222
+ isEnded = true;
223
+ this.emit("DebugLog", {
224
+ type: "common",
225
+ text: `ffmpeg end, reason: ${JSON.stringify(args, (_, v) => (v instanceof Error ? v.stack : v))}`,
226
+ });
227
+ const reason = args[0] instanceof Error ? args[0].message : String(args[0]);
228
+ this.recordHandle?.stop(reason);
229
+ };
230
+ const isInvalidStream = utils.createInvalidStreamChecker();
231
+ const timeoutChecker = utils.createTimeoutChecker(() => onEnd("ffmpeg timeout"), 10e3);
232
+ const command = createFFMPEGBuilder(stream.url)
233
+ .inputOptions("-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")
234
+ .outputOptions(ffmpegOutputOptions)
235
+ .output(streamManager.videoFilePath)
236
+ .on("start", () => {
237
+ streamManager.handleVideoStarted();
238
+ })
239
+ .on("error", onEnd)
240
+ .on("end", () => onEnd("finished"))
241
+ .on("stderr", async (stderrLine) => {
242
+ assert(typeof stderrLine === "string");
243
+ if (utils.isFfmpegStartSegment(stderrLine)) {
244
+ await streamManager.handleVideoStarted(stderrLine);
245
+ }
246
+ // TODO:解析时间
247
+ this.emit("DebugLog", { type: "ffmpeg", text: stderrLine });
248
+ if (isInvalidStream(stderrLine)) {
249
+ onEnd("invalid stream");
250
+ }
251
+ })
252
+ .on("stderr", timeoutChecker.update);
253
+ if (hasSegment) {
254
+ command.outputOptions("-f", "segment", "-segment_time", String(this.segment * 60), "-reset_timestamps", "1");
255
+ }
256
+ const ffmpegArgs = command._getArguments();
257
+ command.run();
258
+ // TODO: 需要一个机制防止空录制,比如检查文件的大小变化、ffmpeg 的输出、直播状态等
259
+ const stop = utils.singleton(async (reason) => {
260
+ if (!this.recordHandle)
261
+ return;
262
+ this.state = "stopping-record";
263
+ // TODO: emit update event
264
+ timeoutChecker.stop();
265
+ try {
266
+ // @ts-ignore
267
+ command.ffmpegProc?.stdin?.write("q");
268
+ // TODO: 这里可能会有内存泄露,因为事件还没清,之后再检查下看看。
269
+ client.stop();
270
+ }
271
+ catch (err) {
272
+ // TODO: 这个 stop 经常报错,这里先把错误吞掉,以后再处理。
273
+ this.emit("DebugLog", { type: "common", text: String(err) });
274
+ }
275
+ this.usedStream = undefined;
276
+ this.usedSource = undefined;
277
+ // TODO: other codes
278
+ // TODO: emit update event
279
+ await streamManager.handleVideoCompleted();
280
+ this.emit("RecordStop", { recordHandle: this.recordHandle, reason });
281
+ this.off("videoFileCreated", handleVideoCreated);
282
+ this.recordHandle = undefined;
283
+ this.liveInfo = undefined;
284
+ this.state = "idle";
285
+ this.qualityRetry = this.qualityMaxRetry;
286
+ });
287
+ this.recordHandle = {
288
+ id: genRecordUUID(),
289
+ stream: stream.name,
290
+ source: stream.source,
291
+ url: stream.url,
292
+ ffmpegArgs,
293
+ savePath: savePath,
294
+ stop,
295
+ };
296
+ this.emit("RecordStart", this.recordHandle);
297
+ return this.recordHandle;
298
+ };
299
+ export const provider = {
300
+ id: "DouYu",
301
+ name: "斗鱼",
302
+ siteURL: "https://douyu.com/",
303
+ matchURL(channelURL) {
304
+ return /https?:\/\/(?:.*?\.)?douyu.com\//.test(channelURL);
305
+ },
306
+ async resolveChannelInfoFromURL(channelURL) {
307
+ if (!this.matchURL(channelURL))
308
+ return null;
309
+ channelURL = channelURL.trim();
310
+ const res = await requester.get(channelURL);
311
+ const html = res.data;
312
+ const matched = html.match(/\$ROOM\.room_id.?=(.*?);/);
313
+ if (!matched)
314
+ return null;
315
+ const room_id = matched[1].trim();
316
+ const roomInfo = await getRoomInfo(Number(room_id));
317
+ return {
318
+ id: matched[1].trim(),
319
+ title: roomInfo.room.room_name,
320
+ owner: roomInfo.room.nickname,
321
+ };
322
+ },
323
+ createRecorder(opts) {
324
+ return createRecorder({ providerId: provider.id, ...opts });
325
+ },
326
+ fromJSON(recorder) {
327
+ return defaultFromJSON(this, recorder);
328
+ },
329
+ setFFMPEGOutputArgs(args) {
330
+ ffmpegOutputOptions.splice(0, ffmpegOutputOptions.length, ...args);
331
+ },
332
+ };
333
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import axios from "axios";
2
+ export declare const requester: axios.AxiosInstance;
@@ -0,0 +1,8 @@
1
+ import axios from "axios";
2
+ export const requester = axios.create({
3
+ timeout: 10e3,
4
+ // axios 会自动读取环境变量中的 http_proxy 和 https_proxy 并应用,
5
+ // 但会导致请求报错 "Client network socket disconnected before secure TLS connection was established"。
6
+ proxy: false,
7
+ });
8
+ //# sourceMappingURL=requester.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"requester.js","sourceRoot":"","sources":["../src/requester.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,SAAS,GAAG,KAAK,CAAC,MAAM,CAAC;IACpC,OAAO,EAAE,IAAI;IACb,kDAAkD;IAClD,8FAA8F;IAC9F,KAAK,EAAE,KAAK;CACb,CAAC,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { Recorder } from "@bililive-tools/manager";
2
+ export declare function getInfo(channelId: string): Promise<{
3
+ living: boolean;
4
+ owner: string;
5
+ title: string;
6
+ startTime: Date;
7
+ avatar: string;
8
+ cover: string;
9
+ liveId: string;
10
+ }>;
11
+ export declare function getStream(opts: Pick<Recorder, "channelId" | "quality"> & {
12
+ rejectCache?: boolean;
13
+ strictQuality?: boolean;
14
+ }): Promise<{
15
+ living: true;
16
+ sources: import("./dy_api.js").SourceProfile[];
17
+ streams: import("./dy_api.js").StreamProfile[];
18
+ isSupportRateSwitch: boolean;
19
+ isOriginalStream: boolean;
20
+ currentStream: {
21
+ source: string;
22
+ name: string;
23
+ rate: number;
24
+ url: string;
25
+ };
26
+ }>;
package/lib/stream.js ADDED
@@ -0,0 +1,81 @@
1
+ import { live } from "douyu-api";
2
+ import { DouyuQualities, utils } from "@bililive-tools/manager";
3
+ import { getLiveInfo } from "./dy_api.js";
4
+ import { requester } from "./requester.js";
5
+ export async function getInfo(channelId) {
6
+ const res = await requester.get(`http://open.douyucdn.cn/api/RoomApi/room/${channelId}`);
7
+ if (res.status !== 200) {
8
+ if (res.status === 404 && res.data === "Not Found") {
9
+ throw new Error("错误的地址 " + channelId);
10
+ }
11
+ throw new Error(`Unexpected status code, ${res.status}, ${res.data}`);
12
+ }
13
+ if (typeof res.data !== "object")
14
+ throw new Error(`Unexpected response, ${res.status}, ${res.data}`);
15
+ const json = res.data;
16
+ if (json.error === 101)
17
+ throw new Error("错误的地址 " + channelId);
18
+ if (json.error !== 0)
19
+ throw new Error("Unexpected error code, " + json.error);
20
+ let living = json.data.room_status === "1";
21
+ const data = await live.getRoomInfo(Number(channelId));
22
+ if (living) {
23
+ const isVideoLoop = data.room.videoLoop === 1;
24
+ if (isVideoLoop) {
25
+ living = false;
26
+ }
27
+ }
28
+ const startTime = new Date(data.room.show_time * 1000);
29
+ return {
30
+ living,
31
+ owner: data.room.nickname,
32
+ title: data.room.room_name,
33
+ avatar: data.room.avatar.big,
34
+ cover: data.room.room_pic,
35
+ startTime: startTime,
36
+ liveId: utils.md5(`${channelId}-${startTime?.getTime() ?? Date.now()}`),
37
+ // gifts: data.gift.map((g) => ({
38
+ // id: g.id,
39
+ // name: g.name,
40
+ // img: g.himg,
41
+ // cost: g.pc,
42
+ // })),
43
+ };
44
+ }
45
+ export async function getStream(opts) {
46
+ const qn = (DouyuQualities.includes(opts.quality) ? opts.quality : 0);
47
+ let liveInfo = await getLiveInfo({
48
+ channelId: opts.channelId,
49
+ rate: qn,
50
+ });
51
+ if (!liveInfo.living)
52
+ throw new Error("It must be called getStream when living");
53
+ // console.log(JSON.stringify(liveInfo, null, 2));
54
+ if (liveInfo.currentStream.rate !== qn && opts.strictQuality) {
55
+ throw new Error("Can not get expect quality because of strictQuality");
56
+ }
57
+ let expectSource = liveInfo.sources[0];
58
+ if (!expectSource) {
59
+ throw new Error("Source list is empty");
60
+ }
61
+ // 是否存在画质下没有source的情况,可能需要切换画质
62
+ if (liveInfo.currentStream.rate !== qn) {
63
+ if (liveInfo.streams.length === 0) {
64
+ throw new Error("Can not get expect quality because of no available stream");
65
+ }
66
+ else {
67
+ liveInfo = await getLiveInfo({
68
+ channelId: opts.channelId,
69
+ rate: liveInfo.streams[0].rate,
70
+ });
71
+ if (!liveInfo.living)
72
+ throw new Error("It must be called getStream when living");
73
+ }
74
+ }
75
+ // 流未准备好,防止刚开播时的无效录制。
76
+ // 该判断可能导致开播前 30 秒左右无法录制到,因为 streamStatus 在后端似乎有缓存,所以暂时不使用。
77
+ // TODO: 需要在 ffmpeg 那里加处理,防止无效录制
78
+ // if (!json.data.streamStatus) return
79
+ return liveInfo;
80
+ }
81
+ //# sourceMappingURL=stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream.js","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"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"}