Skip to content
This repository was archived by the owner on Nov 12, 2024. It is now read-only.

Commit 0a0880b

Browse files
authored
Merge pull request #2 from 1962247851/dev
Dev
2 parents 71a908c + dcdf3bc commit 0a0880b

File tree

5 files changed

+142
-222
lines changed

5 files changed

+142
-222
lines changed

README.md

Lines changed: 23 additions & 214 deletions
Original file line numberDiff line numberDiff line change
@@ -1,235 +1,44 @@
11
# ordinaryroad-bilibili-live
22

3+
![license](https://img.shields.io/github/license/1962247851/ordinaryroad-bilibili-live) ![release](https://img.shields.io/github/v/release/1962247851/ordinaryroad-bilibili-live)
4+
35
使用Netty来连接B站直播间的弹幕信息流Websocket接口
46

57
- Feature 0: Netty
68
- Feature 1: 消息中的未知属性统一放到单独的MAP中
79
- Feature 2: 支持房间短id
810

9-
example请看`BilibiliBinaryFrameHandlerTest`测试类
11+
### 1. 引入依赖
12+
13+
```xml
14+
15+
<dependency>
16+
<groupId>tech.ordinaryroad.bilibili.live</groupId>
17+
<artifactId>ordinaryroad-bilibili-live</artifactId>
18+
<!-- 参考github release版本,不需要前缀`v` -->
19+
<version>${ordinaryroad-bilibili-live.version}</version>
20+
</dependency>
21+
```
22+
23+
### 2. 开始使用
24+
25+
> 参考`BilibiliBinaryFrameHandlerTest`测试类
26+
27+
重写`IBilibiliSendSmsReplyMsgListener`中的方法,进行处理业务逻辑(耗时操作可能需要异步)
28+
29+
### BilibiliBinaryFrameHandlerTest
1030

1131
修改创建认证包方法的参数后,运行查看效果
1232

1333
> 创建发送认证包
1434
![创建认证包](example/createAuth.png)
35+
1536
> 控制台输出示例
1637
![控制台示例](example/console.png)
38+
1739
> 注:目前protover仅支持2(普通包正文使用zlib压缩)
1840
> CmdEnum可能不全,需要根据控制台信息手动补(不影响运行)
1941
20-
### Bilibili协议编解码工具类`BilibiliCodecUtil`
21-
22-
```java
23-
24-
@Slf4j
25-
public class BilibiliCodecUtil {
26-
27-
public static final short FRAME_HEADER_LENGTH = 16;
28-
29-
public static ByteBuf encode(BaseBilibiliMsg msg) {
30-
ByteBuf out = Unpooled.buffer(100);
31-
String bodyJsonString = msg.toString();
32-
// HeartbeatMsg不需要正文,如果序列化后得到`{}`,则替换为空字符串
33-
if ("{}".equals(bodyJsonString)) {
34-
bodyJsonString = "";
35-
}
36-
byte[] bodyBytes = bodyJsonString.getBytes(StandardCharsets.UTF_8);
37-
int length = bodyBytes.length + FRAME_HEADER_LENGTH;
38-
out.writeInt(length);
39-
out.writeShort(FRAME_HEADER_LENGTH);
40-
out.writeShort(msg.getProtoverEnum().getCode());
41-
out.writeInt(msg.getOperationEnum().getCode());
42-
out.writeInt(BaseBilibiliMsg.sequence++);
43-
out.writeBytes(bodyBytes);
44-
return out;
45-
}
46-
47-
public static List<BaseBilibiliMsg> decode(ByteBuf in) {
48-
List<BaseBilibiliMsg> msgList = new ArrayList<>();
49-
Queue<ByteBuf> pendingByteBuf = new LinkedList<>();
50-
51-
do {
52-
Optional<BaseBilibiliMsg> msg = doDecode(in, pendingByteBuf);
53-
msg.ifPresent(msgList::add);
54-
in = pendingByteBuf.poll();
55-
} while (in != null);
56-
57-
return msgList;
58-
}
59-
60-
/**
61-
* 执行解码操作,有压缩则先解压,解压后可能得到多条消息
62-
*
63-
* @param in handler收到的一条消息
64-
* @param pendingByteBuf 用于存放未读取完的ByteBuf
65-
* @return Optional<BaseBilibiliMsg> 何时为空值:不支持的{@link OperationEnum},不支持的{@link ProtoverEnum},{@link #parse(OperationEnum, String)}反序列化失败
66-
* @see OperationEnum
67-
* @see ProtoverEnum
68-
*/
69-
private static Optional<BaseBilibiliMsg> doDecode(ByteBuf in, Queue<ByteBuf> pendingByteBuf) {
70-
int length = in.readInt();
71-
short frameHeaderLength = in.readShort();
72-
short protoverCode = in.readShort();
73-
int operationCode = in.readInt();
74-
int sequence = in.readInt();
75-
int contentLength = length - frameHeaderLength;
76-
byte[] inputBytes = new byte[contentLength];
77-
in.readBytes(inputBytes);
78-
if (in.readableBytes() != 0) {
79-
pendingByteBuf.offer(in);
80-
}
81-
82-
OperationEnum operationEnum = OperationEnum.getByCode(operationCode);
83-
if (protoverCode == ProtoverEnum.NORMAL_ZLIB.getCode()) {
84-
switch (operationEnum) {
85-
case SEND_SMS_REPLY -> {
86-
// Decompress the bytes
87-
Inflater inflater = new Inflater();
88-
inflater.reset();
89-
inflater.setInput(inputBytes);
90-
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(contentLength);
91-
try {
92-
byte[] bytes = new byte[1024];
93-
while (!inflater.finished()) {
94-
int count = inflater.inflate(bytes);
95-
byteArrayOutputStream.write(bytes, 0, count);
96-
}
97-
} catch (DataFormatException e) {
98-
throw new RuntimeException(e);
99-
}
100-
inflater.end();
101-
102-
return doDecode(Unpooled.wrappedBuffer(byteArrayOutputStream.toByteArray()), pendingByteBuf);
103-
}
104-
case HEARTBEAT_REPLY -> {
105-
BigInteger bigInteger = new BigInteger(inputBytes, 0, 4);
106-
return parse(operationEnum, "{\"popularity\":%d}".formatted(bigInteger));
107-
}
108-
default -> {
109-
System.out.println("operationCode = " + operationCode);
110-
String s = new String(inputBytes, StandardCharsets.UTF_8);
111-
return parse(operationEnum, s);
112-
}
113-
}
114-
} else if (protoverCode == ProtoverEnum.NORMAL_NO_COMPRESSION.getCode()) {
115-
String s = new String(inputBytes, StandardCharsets.UTF_8);
116-
return parse(operationEnum, s);
117-
} else {
118-
log.warn("暂不支持的版本:{}", protoverCode);
119-
return Optional.empty();
120-
}
121-
}
122-
123-
public static Optional<BaseBilibiliMsg> parse(OperationEnum operation, String jsonString) {
124-
switch (operation) {
125-
case SEND_SMS_REPLY -> {
126-
try {
127-
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, SendSmsReplyMsg.class));
128-
} catch (JsonProcessingException e) {
129-
throw new RuntimeException(e);
130-
}
131-
}
132-
case AUTH_REPLY -> {
133-
try {
134-
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, AuthReplyMsg.class));
135-
} catch (JsonProcessingException e) {
136-
throw new RuntimeException(e);
137-
}
138-
}
139-
case HEARTBEAT_REPLY -> {
140-
try {
141-
return Optional.ofNullable(BaseBilibiliMsg.OBJECT_MAPPER.readValue(jsonString, HeartbeatReplyMsg.class));
142-
} catch (JsonProcessingException e) {
143-
throw new RuntimeException(e);
144-
}
145-
}
146-
default -> {
147-
log.warn("暂不支持 {}", operation);
148-
return Optional.empty();
149-
}
150-
}
151-
}
152-
153-
}
154-
```
155-
156-
### Bilibili信息流回掉接口`IBilibiliSendSmsReplyMsgListener`
157-
158-
```java
159-
public interface IBilibiliSendSmsReplyMsgListener {
160-
161-
/**
162-
* 收到弹幕
163-
*
164-
* @param msg SendSmsReplyMsg
165-
*/
166-
void onDanmuMsg(SendSmsReplyMsg msg);
167-
168-
/**
169-
* 收到礼物
170-
*
171-
* @param msg SendSmsReplyMsg
172-
*/
173-
void onSendGift(SendSmsReplyMsg msg);
174-
175-
/**
176-
* 普通用户进入直播间
177-
*
178-
* @param msg SendSmsReplyMsg
179-
*/
180-
void onEnterRoom(SendSmsReplyMsg msg);
181-
182-
/**
183-
* 入场效果(高能用户)
184-
*
185-
* @param sendSmsReplyMsg SendSmsReplyMsg
186-
*/
187-
void onEntryEffect(SendSmsReplyMsg sendSmsReplyMsg);
188-
189-
/**
190-
* 观看人数变化
191-
*
192-
* @param msg SendSmsReplyMsg
193-
*/
194-
void onWatchedChange(SendSmsReplyMsg msg);
195-
196-
/**
197-
* 为主播点赞
198-
*
199-
* @param msg SendSmsReplyMsg
200-
*/
201-
void onClickLike(SendSmsReplyMsg msg);
202-
203-
/**
204-
* 点赞数更新
205-
*
206-
* @param msg SendSmsReplyMsg
207-
*/
208-
void onClickUpdate(SendSmsReplyMsg msg);
209-
210-
/**
211-
* 其他消息
212-
*
213-
* @param cmd CmdEnum
214-
* @param msg SendSmsReplyMsg
215-
*/
216-
default void onOtherSendSmsReplyMsg(CmdEnum cmd, SendSmsReplyMsg msg) {
217-
// ignore
218-
}
219-
220-
/**
221-
* 未知cmd
222-
*
223-
* @param cmdString 实际收到的cmd字符串
224-
* @param msg SendSmsReplyMsg
225-
*/
226-
default void onUnknownCmd(String cmdString, SendSmsReplyMsg msg) {
227-
// ignore
228-
}
229-
}
230-
231-
```
232-
23342
### 相关链接
23443

23544
- [B站直播数据包分析连载(2018-12-11更新)_weixin_34009794的博客-CSDN博客](https://blog.csdn.net/weixin_34009794/article/details/88689474)

pom.xml

Lines changed: 89 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,31 @@
2929

3030
<groupId>tech.ordinaryroad.bilibili.live</groupId>
3131
<artifactId>ordinaryroad-bilibili-live</artifactId>
32-
<version>0.0.3</version>
32+
<version>0.0.4</version>
3333
<packaging>jar</packaging>
3434
<name>ordinaryroad-bilibili-live</name>
35-
<description>ordinaryroad-bilibili-live</description>
35+
<description>使用Netty来连接B站直播间的弹幕信息流Websocket接口</description>
36+
<url>https://github.com/1962247851/ordinaryroad-bilibili-live</url>
37+
<licenses>
38+
<license>
39+
<name>The MIT License</name>
40+
<url>https://opensource.org/license/mit/</url>
41+
<distribution>repo</distribution>
42+
</license>
43+
</licenses>
44+
<scm>
45+
<url>https://github.com/1962247851/ordinaryroad-bilibili-live</url>
46+
<connection>scm:git:https://github.com/1962247851/ordinaryroad-bilibili-live.git</connection>
47+
<developerConnection>scm:git:https://github.com/1962247851/ordinaryroad-bilibili-live</developerConnection>
48+
</scm>
49+
<developers>
50+
<developer>
51+
<name>OrdinaryRoad</name>
52+
<email>or-mjz@qq.com</email>
53+
<url>https://github.com/1962247851</url>
54+
<timezone>UTC+08:00</timezone>
55+
</developer>
56+
</developers>
3657

3758
<properties>
3859
<maven.compiler.source>17</maven.compiler.source>
@@ -114,6 +135,72 @@
114135
</annotationProcessorPaths>
115136
</configuration>
116137
</plugin>
138+
139+
<!-- 发包相关插件-start -->
140+
<plugin>
141+
<groupId>org.apache.maven.plugins</groupId>
142+
<artifactId>maven-source-plugin</artifactId>
143+
<version>${maven-source-plugin.version}</version>
144+
<executions>
145+
<execution>
146+
<id>attach-sources</id>
147+
<goals>
148+
<goal>jar-no-fork</goal>
149+
</goals>
150+
</execution>
151+
</executions>
152+
</plugin>
153+
<plugin>
154+
<groupId>org.apache.maven.plugins</groupId>
155+
<artifactId>maven-javadoc-plugin</artifactId>
156+
<version>${maven-javadoc-plugin.version}</version>
157+
<configuration>
158+
<encoding>UTF-8</encoding>
159+
<charset>UTF-8</charset>
160+
<docencoding>UTF-8</docencoding>
161+
</configuration>
162+
<executions>
163+
<execution>
164+
<id>attach-javadocs</id>
165+
<goals>
166+
<goal>jar</goal>
167+
</goals>
168+
<configuration>
169+
<additionalJOption>${javadoc.opts}</additionalJOption>
170+
</configuration>
171+
</execution>
172+
</executions>
173+
</plugin>
174+
<plugin>
175+
<groupId>org.apache.maven.plugins</groupId>
176+
<artifactId>maven-gpg-plugin</artifactId>
177+
<version>${maven-gpg-plugin.version}</version>
178+
<executions>
179+
<execution>
180+
<id>sign-artifacts</id>
181+
<phase>verify</phase>
182+
<goals>
183+
<goal>sign</goal>
184+
</goals>
185+
<configuration>
186+
<keyname>${gpg.keyname}</keyname>
187+
<passphraseServerId>${gpg.keyname}</passphraseServerId>
188+
</configuration>
189+
</execution>
190+
</executions>
191+
</plugin>
192+
<plugin>
193+
<groupId>org.sonatype.plugins</groupId>
194+
<artifactId>nexus-staging-maven-plugin</artifactId>
195+
<version>${nexus-staging-maven-plugin.version}</version>
196+
<extensions>true</extensions>
197+
<configuration>
198+
<serverId>ossrh</serverId>
199+
<nexusUrl>https://s01.oss.sonatype.org/</nexusUrl>
200+
<autoReleaseAfterClose>${auto-release-after-close}</autoReleaseAfterClose>
201+
</configuration>
202+
</plugin>
203+
<!-- 发包相关插件-end -->
117204
</plugins>
118205
</build>
119206
</project>

0 commit comments

Comments
 (0)