├── .gitignore
├── LICENSE
├── README.MD
└── Src
├── BilibiliUtilities.Live
├── BilibiliUtilities.Live.csproj
├── Lib
│ ├── DanmuHead.cs
│ ├── IMessageDispatcher.cs
│ ├── IMessageHandler.cs
│ └── MessageDispatcher.cs
├── LiveRoom.cs
└── Message
│ ├── BaseMessage.cs
│ ├── ComboEndMessage.cs
│ ├── DanmuMessage.cs
│ ├── EntryEffectMessage.cs
│ ├── GiftMessage.cs
│ ├── GuardBuyMessage.cs
│ ├── InteractWordMessage.cs
│ ├── NoticeMessage.cs
│ ├── RoomChangeMessage.cs
│ ├── RoomRankMessage.cs
│ ├── RoomUpdateMessage.cs
│ ├── UserToastMessage.cs
│ ├── WelcomeGuardMessage.cs
│ └── WelcomeMessage.cs
├── BilibiliUtilities.Test
├── BilibiliUtilities.Test.csproj
├── LiveLib
│ └── LiveHandler.cs
└── Tests
│ ├── LiveTest.cs
│ └── VideoTest.cs
├── BilibiliUtilities.Utils
├── BilibiliUtilities - Backup.Utils.csproj
├── BilibiliUtilities.Utils.csproj
├── CentralUtils
│ └── VideoUtil.cs
├── LiveUtils
│ └── RoomUtil.cs
└── UserUtils
│ └── UserUtil.cs
├── BilibiliUtilities.sln
└── EndianBitConverter
├── BigEndianBitConverter.cs
├── EndianBitConverter.cs
├── EndianBitConverter.csproj
├── LittleEndianBitConverter.cs
├── Properties
└── AssemblyInfo.cs
└── SingleConverter.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | # Visual Studio
2 |
3 | # User-specific files
4 | *.suo
5 | *.user
6 | *.userosscache
7 | *.sln.docstates
8 |
9 | # User-specific files (MonoDevelop/Xamarin Studio)
10 | *.userprefs
11 |
12 | # Build results
13 | [Dd]ebug/
14 | [Dd]ebugPublic/
15 | [Rr]elease/
16 | [Rr]eleases/
17 | [Xx]64/
18 | [Xx]86/
19 | [Bb]uild/
20 | bld/
21 | [Bb]in/
22 | [Oo]bj/
23 |
24 | # Visual Studio 2015 cache/options directory
25 | .vs/
26 | # Uncomment if you have tasks that create the project's static files in wwwroot
27 | #wwwroot/
28 |
29 | # MSTest test Results
30 | [Tt]est[Rr]esult*/
31 | [Bb]uild[Ll]og.*
32 |
33 | # NUNIT
34 | *.VisualState.xml
35 | TestResult.xml
36 |
37 | # Build Results of an ATL Project
38 | [Dd]ebugPS/
39 | [Rr]eleasePS/
40 | dlldata.c
41 |
42 | # DNX
43 | project.lock.json
44 | artifacts/
45 |
46 | *_i.c
47 | *_p.c
48 | *_i.h
49 | *.ilk
50 | *.meta
51 | *.obj
52 | *.bin
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 | *.VC.VC.opendb
85 |
86 | # Visual Studio profiler
87 | *.psess
88 | *.vsp
89 | *.vspx
90 | *.sap
91 |
92 | # TFS 2012 Local Workspace
93 | $tf/
94 |
95 | # Guidance Automation Toolkit
96 | *.gpState
97 |
98 | # ReSharper is a .NET coding add-in
99 | _ReSharper*/
100 | *.[Rr]e[Ss]harper
101 | *.DotSettings.user
102 |
103 | # JustCode is a .NET coding add-in
104 | .JustCode
105 |
106 | # TeamCity is a build add-in
107 | _TeamCity*
108 |
109 | # DotCover is a Code Coverage Tool
110 | *.dotCover
111 |
112 | # NCrunch
113 | _NCrunch_*
114 | .*crunch*.local.xml
115 | nCrunchTemp_*
116 |
117 | # MightyMoose
118 | *.mm.*
119 | AutoTest.Net/
120 |
121 | # Web workbench (sass)
122 | .sass-cache/
123 |
124 | # Installshield output folder
125 | [Ee]xpress/
126 |
127 | # DocProject is a documentation generator add-in
128 | DocProject/buildhelp/
129 | DocProject/Help/*.HxT
130 | DocProject/Help/*.HxC
131 | DocProject/Help/*.hhc
132 | DocProject/Help/*.hhk
133 | DocProject/Help/*.hhp
134 | DocProject/Help/Html2
135 | DocProject/Help/html
136 |
137 | # Click-Once directory
138 | publish/
139 |
140 | # Publish Web Output
141 | *.[Pp]ublish.xml
142 | *.azurePubxml
143 | *.publishproj
144 | *.pubxml
145 | PublishProfiles/
146 | PublishScripts/
147 |
148 | # NuGet Packages
149 | *.nupkg
150 | # The packages folder can be ignored because of Package Restore
151 | **/packages/*
152 | # except build/, which is used as an MSBuild target.
153 | !**/packages/build/
154 | # Uncomment if necessary however generally it will be regenerated when needed
155 | #!**/packages/repositories.config
156 | # NuGet v3's project.json files produces more ignoreable files
157 | *.nuget.props
158 | *.nuget.targets
159 |
160 | # Microsoft Azure Build Output
161 | csx/
162 | *.build.csdef
163 |
164 | # Microsoft Azure Emulator
165 | ecf/
166 | rcf/
167 |
168 | # Windows Store app package directory
169 | AppPackages/
170 | BundleArtifacts/
171 | Package.StoreAssociation.xml
172 | _pkginfo.txt
173 |
174 | # Visual Studio cache files
175 | # files ending in .cache can be ignored
176 | *.[Cc]ache
177 | # but keep track of directories ending in .cache
178 | !*.[Cc]ache/
179 |
180 | # Others
181 | ClientBin/
182 | [Ss]tyle[Cc]op.*
183 | ~$*
184 | *~
185 | *.dbmdl
186 | *.dbproj.schemaview
187 | *.pfx
188 | *.publishsettings
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 | paket-files/
241 |
242 | # FAKE - F# Make
243 | .fake/
244 |
245 | .DS_Store
246 |
247 | .idea
248 | /Bilibili.sln
249 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 is-a-gamer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | The project repository is only for China, so the README and code comments will be in Chinese
2 | ---
3 | BilibiliUtilities.Live [](https://www.nuget.org/packages/BilibiliUtilities.Live/)
4 |
5 | BilibiliUtilities.Utils [](https://www.nuget.org/packages/BilibiliUtilities.Live/)
6 | # **该项目已经暂停维护**
7 |
8 | 项目早已暂停, B站官方SDK也已经支持Unity,此项目将存档,如有类似需求,请前往 [哔哩哔哩直播开放平台](https://open-live.bilibili.com/) 查看 [SDK For Unity](https://open-live.bilibili.com/document/cca272c5-e49d-6bea-0fbe-23c37288a08a)
9 |
10 | ### 说明
11 | 一个希望将B站相关的数据获取到的包,现在还只有直播间的信息,比如直播间的弹幕 直播间的人气值
12 |
13 | 目前还是一个非常初始的版本,希望能帮助我寻找BUG,非常感谢
14 |
15 | 使用的示例代码在BilibiliUtilities.Test中,并且写了注释
16 |
17 | `更详细的说明请阅读源码`
18 |
19 | ```
20 | BilibiliUtilities.Live为直播间代码
21 | BilibiliUtilities.Test为使用测试代码
22 | BilibiliUtilities.Utils为其他模块提供代码
23 | ```
24 |
25 | ### 安装
26 | ```bash
27 | dotnet add package BilibiliUtilities.Live --version 0.0.1
28 | dotnet add package BilibiliUtilities.Utils --version 0.0.1
29 | ```
30 |
31 | ### 使用
32 |
33 | [视频演示](https://www.bilibili.com/video/BV1af4y1v7Fd)
34 |
35 | 如果有BUG可以在issue中提出
36 |
37 | 如果你想支持一下,可以点击右上角的star
38 |
39 | Bilibili UID:27609142
40 |
41 |
42 | ___
43 | `EndianBitConverter` 来自 https://github.com/davidrea-MS/BitConverter
44 |
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/BilibiliUtilities.Live.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | 0.0.2
6 | BilibiliUtilities-Live
7 | Provide support for real-time message distribution in bilibili live room
8 | https://github.com/is-a-gamer/BilibiliUtilities
9 | Bilibili
10 | 0.0.3
11 | net471;netstandard2.0;netcoreapp3.1
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Lib/DanmuHead.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace BilibiliUtilities.Live.Lib
4 | {
5 | ///
6 | /// 发送数据的消息的头信息
7 | ///
8 | public class DanmuHead
9 | {
10 | ///
11 | /// 总长度 (协议头 + 数据长度)
12 | ///
13 | public int PacketLength;
14 |
15 | ///
16 | /// 头长度
17 | ///
18 | public short HeaderLength;
19 |
20 | ///
21 | /// 版本
22 | ///
23 | public short Version;
24 |
25 | ///
26 | /// 操作类型 (消息类型)
27 | ///
28 | public int Action;
29 |
30 | ///
31 | /// 参数, 固定为1
32 | ///
33 | public int Parameter;
34 |
35 | ///
36 | /// 将从流中读取的内容转换为消息头
37 | ///
38 | ///
39 | ///
40 | ///
41 | public static DanmuHead BufferToDanmuHead(byte[] buffer)
42 | {
43 | if (buffer.Length < 16)
44 | {
45 | throw new ArgumentException();
46 | }
47 |
48 | return new DanmuHead
49 | {
50 | PacketLength = EndianBitConverter.EndianBitConverter.BigEndian.ToInt32(buffer, 0),
51 | HeaderLength = EndianBitConverter.EndianBitConverter.BigEndian.ToInt16(buffer, 4),
52 | Version = EndianBitConverter.EndianBitConverter.BigEndian.ToInt16(buffer, 6),
53 | Action = EndianBitConverter.EndianBitConverter.BigEndian.ToInt32(buffer, 8),
54 | Parameter = EndianBitConverter.EndianBitConverter.BigEndian.ToInt32(buffer, 12),
55 | };
56 | }
57 |
58 | ///
59 | /// 计算数据部分的长度
60 | ///
61 | ///
62 | /// PacketLength - HeaderLength 类型数据:int
63 | ///
64 | public int MessageLength()
65 | {
66 | return PacketLength - HeaderLength;
67 | }
68 | }
69 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Lib/IMessageDispatcher.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Newtonsoft.Json.Linq;
3 |
4 | namespace BilibiliUtilities.Live.Lib
5 | {
6 | ///
7 | /// 消息分发器接口
8 | ///
9 | public interface IMessageDispatcher
10 | {
11 | ///
12 | /// 不需要手动调用.只需要实现方法
13 | ///
14 | ///
15 | ///{
16 | ///case "DANMU_MSG":
17 | ///await messageHandler.DanmuMessageHandlerAsync(DanmuMessage.JsonToDanmuMessage(message));
18 | ///}
19 | ///
20 | ///
21 | ///
22 | ///
23 | Task DispatchAsync(JObject message,IMessageHandler messageHandler);
24 | }
25 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Lib/IMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using BilibiliUtilities.Live.Message;
3 |
4 | namespace BilibiliUtilities.Live.Lib
5 | {
6 | ///
7 | /// 消息处理器
8 | ///
9 | public interface IMessageHandler
10 | {
11 | ///
12 | /// 接收以及处理弹幕消息
13 | ///
14 | /// 弹幕消息
15 | ///
16 | Task DanmuMessageHandlerAsync(DanmuMessage danmuMessage);
17 | ///
18 | /// 处理人气值
19 | ///
20 | /// 类型是int,为人气值
21 | ///
22 | Task AudiencesHandlerAsync(int audiences);
23 | ///
24 | /// 处理大范围通知消息,比如有谁的房间被送了小电视
25 | ///
26 | ///
27 | ///
28 | Task NoticeMessageHandlerAsync(NoticeMessage noticeMessage);
29 | ///
30 | /// 礼物消息
31 | ///
32 | ///
33 | ///
34 | Task GiftMessageHandlerAsync(GiftMessage giftMessage);
35 | ///
36 | /// 普通的欢迎信息,比如月费老爷
37 | ///
38 | ///
39 | ///
40 | Task WelcomeMessageHandlerAsync(WelcomeMessage welcomeMessage);
41 | ///
42 | /// 送礼物的连接结束后返回的信息
43 | ///
44 | ///
45 | ///
46 | Task ComboEndMessageHandlerAsync(ComboEndMessage comboEndMessage);
47 | ///
48 | /// 房间的更新信息
49 | ///
50 | ///
51 | ///
52 | Task RoomUpdateMessageHandlerAsync(RoomUpdateMessage roomUpdateMessage);
53 | ///
54 | /// 欢迎房管进入的消息
55 | ///
56 | ///
57 | ///
58 | Task WelcomeGuardMessageHandlerAsync(WelcomeGuardMessage welcomeGuardMessage);
59 | ///
60 | /// 直播间开启的通知
61 | ///
62 | ///
63 | ///
64 | Task LiveStartMessageHandlerAsync(int roomId);
65 | ///
66 | /// 直播间关闭的通知
67 | ///
68 | ///
69 | ///
70 | Task LiveStopMessageHandlerAsync(int roomId);
71 | ///
72 | /// 进入直播间
73 | ///
74 | ///
75 | ///
76 | Task InteractWordMessageHandlerAsync(InteractWordMessage message);
77 | ///
78 | /// 舰长,提督,总督进入房间的通知
79 | ///
80 | ///
81 | ///
82 | Task EntryEffectMessageHandlerAsync(EntryEffectMessage entryEffectMessage);
83 | ///
84 | /// 购买舰长的信息处理
85 | ///
86 | ///
87 | ///
88 | Task GuardBuyMessageHandlerAsync(GuardBuyMessage guardBuyMessage);
89 | ///
90 | /// 现在看,只有购买舰长的信息
91 | ///
92 | ///
93 | ///
94 | Task UserToastMessageHandlerAsync(UserToastMessage userToastMessage);
95 | }
96 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Lib/MessageDispatcher.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Threading.Tasks;
4 | using BilibiliUtilities.Live.Message;
5 | using Newtonsoft.Json.Linq;
6 |
7 | namespace BilibiliUtilities.Live.Lib
8 | {
9 | public class MessageDispatcher : IMessageDispatcher
10 | {
11 | public async Task DispatchAsync(JObject message, IMessageHandler messageHandler)
12 | {
13 | try
14 | {
15 | switch (message["cmd"].ToString())
16 | {
17 | case "DANMU_MSG":
18 | await messageHandler.DanmuMessageHandlerAsync(DanmuMessage.JsonToDanmuMessage(message));
19 | break;
20 | case "SEND_GIFT":
21 | await messageHandler.GiftMessageHandlerAsync(GiftMessage.JsonToGiftMessage(message));
22 | break;
23 | case "GUARD_MSG": // TODO 上舰信息未处理(第一次购买)
24 | Debug.WriteLine("上舰信息");
25 | Debug.WriteLine(message);
26 | break;
27 | case "GUARD_BUY":
28 | await messageHandler.GuardBuyMessageHandlerAsync(GuardBuyMessage.JsonToGuardBuyMessage(message));
29 | break;
30 | case "USER_TOAST_MSG":
31 | await messageHandler.UserToastMessageHandlerAsync(UserToastMessage.JsonToUserToastMessage(message));
32 | break;
33 | case "GUARD_LOTTERY_START": // TODO 上舰抽奖通知
34 | Debug.WriteLine("购买舰长后出现的抽奖");
35 | Debug.WriteLine(message);
36 | break;
37 | case "NOTICE_MSG": // TODO 通知信息未处理
38 | Debug.WriteLine(message);
39 | Debug.WriteLine(message);
40 | break;
41 | case "WELCOME":
42 | await messageHandler.WelcomeMessageHandlerAsync(WelcomeMessage.JsonToWelcomeMessage(message));
43 | break;
44 | case "SYS_MSG": // TODO 系统消息未处理
45 | Debug.WriteLine("SYS_MSG");
46 | Debug.WriteLine(message);
47 | break;
48 | case "COMBO_END":
49 | await messageHandler.ComboEndMessageHandlerAsync(ComboEndMessage.JsonToComboEndMessage(message));
50 | break;
51 | case "SUPER_CHAT_MESSAGE": // TODO 醒目留言信息未处理
52 | break;
53 | case "ROOM_REAL_TIME_MESSAGE_UPDATE":
54 | await messageHandler.RoomUpdateMessageHandlerAsync(RoomUpdateMessage.JsonToRoomUpdateMessage(message));
55 | break;
56 | case "SUPER_CHAT_MESSAGE_JPN": // TODO 另一种醒目留言信息未处理
57 | break;
58 | case "WELCOME_GUARD":
59 | await messageHandler.WelcomeGuardMessageHandlerAsync(WelcomeGuardMessage.JsonToWelcomeGuardMessage(message));
60 | break;
61 | case "ROOM_RANK": // TODO 房间排行信息未处理
62 | break;
63 | case "ENTRY_EFFECT": // TODO 貌似是舰长的进入信息
64 | await messageHandler.EntryEffectMessageHandlerAsync(EntryEffectMessage.JsonToEntryEffectMessage(message));
65 | break;
66 | case "COMBO_SEND": // TODO COMBO_SEND
67 | await messageHandler.ComboEndMessageHandlerAsync(ComboEndMessage.JsonToComboEndMessage(message));
68 | break;
69 | case "ANCHOR_LOT_START": // TODO 天选时刻
70 | Debug.WriteLine("ANCHOR_LOT_START");
71 | Debug.WriteLine(message);
72 | break;
73 | case "ACTIVITY_BANNER_UPDATE_V2":
74 | Debug.WriteLine("ACTIVITY_BANNER_UPDATE_V2");
75 | Debug.WriteLine(message);
76 | break;
77 | case "ROOM_CHANGE": //
78 | Debug.WriteLine("ROOM_CHANGE");
79 | Debug.WriteLine(message);
80 | break;
81 | case "WEEK_STAR_CLOCK":
82 | Debug.WriteLine("WEEK_STAR_CLOCK");
83 | Debug.WriteLine(message);
84 | break;
85 | case "LIVE":
86 | await messageHandler.LiveStartMessageHandlerAsync(int.Parse(message["roomid"].ToString()));
87 | break;
88 | case "PREPARING":
89 | await messageHandler.LiveStopMessageHandlerAsync(int.Parse(message["roomid"].ToString()));
90 | break;
91 | case "INTERACT_WORD":
92 | await messageHandler.InteractWordMessageHandlerAsync(InteractWordMessage.JsonToInteractWordMessage(message));
93 | break;
94 | case "ONLINERANK": //更新排行榜信息
95 | Debug.WriteLine("ONLINERANK");
96 | Debug.WriteLine(message);
97 | break;
98 | case "PANEL": //更新分区排行等信息
99 | Debug.WriteLine("PANEL");
100 | Debug.WriteLine(message);
101 | break;
102 | case "ROOM_BANNER":
103 | Debug.WriteLine("ROOM_BANNER");
104 | Debug.WriteLine(message);
105 | break;
106 | default:
107 | Debug.WriteLine("未记录的信息");
108 | Debug.WriteLine(message);
109 | break;
110 | }
111 | }
112 | catch (ArgumentException e)
113 | {
114 | Debug.WriteLine(e);
115 | throw;
116 | }
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/LiveRoom.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Net.Http;
7 | using System.Net.Sockets;
8 | using System.Text;
9 | using System.Threading.Tasks;
10 | using BilibiliUtilities.Live.Lib;
11 | using EndianBitConverter;
12 | using Newtonsoft.Json;
13 | using Newtonsoft.Json.Linq;
14 | using BilibiliUtilities.Utils.LiveUtils;
15 |
16 | namespace BilibiliUtilities.Live
17 | {
18 | public class LiveRoom
19 | {
20 | //直播页面的房间ID
21 | private int _shotRoomId;
22 |
23 | //真正的直播间ID
24 | private int _roomId;
25 | private readonly TcpClient _tcpClient = new TcpClient();
26 |
27 | private Stream _roomStream;
28 |
29 | //消息版本号,现在固定为2
30 | private const short ProtocolVersion = 2;
31 |
32 | //消息头的长度,现在版本固定为16
33 | //DanmuHead的方法BufferToDanmuHead中有个写死的16,如果后续有修改要一起修改
34 | private const int ProtocolHeadLength = 16;
35 | private readonly IMessageHandler _messageHandler;
36 |
37 | private readonly IMessageDispatcher _messageDispatcher;
38 |
39 | //连接状态
40 | private bool _connected = false;
41 |
42 | public LiveRoom(int roomId, IMessageHandler messageHandler)
43 | {
44 | _shotRoomId = roomId;
45 | _messageDispatcher = new MessageDispatcher();
46 | _messageHandler = messageHandler;
47 | }
48 |
49 | public LiveRoom(int roomId, IMessageHandler messageHandler, IMessageDispatcher messageDispatcher)
50 | {
51 | _shotRoomId = roomId;
52 | _messageDispatcher = messageDispatcher;
53 | _messageHandler = messageHandler;
54 | }
55 |
56 | ///
57 | /// 开启连接
58 | ///
59 | ///
60 | public async Task ConnectAsync()
61 | {
62 | _roomId = await RoomUtil.GetLongRoomId(_shotRoomId);
63 | if (_roomId == 0)
64 | {
65 | return false;
66 | }
67 | var token = await RoomUtil.GetRoomTokenByShortRoomId(_shotRoomId);
68 | if (token.Equals(""))
69 | {
70 | return false;
71 | }
72 | if (!await RoomUtil.ConnectRoomByShortRoomId(_tcpClient, _shotRoomId))
73 | {
74 | return false;
75 | }
76 | _roomStream = _tcpClient.GetStream();
77 | if (!await SendJoinMsgAsync(token))
78 | {
79 | //这是错误处理的代码
80 | return false;
81 | }
82 |
83 | var headBuffer = new byte[ProtocolHeadLength];
84 | await _roomStream.ReadAsync(headBuffer, 0, headBuffer.Length);
85 | DanmuHead danmuHead = DanmuHead.BufferToDanmuHead(headBuffer);
86 | if (danmuHead.HeaderLength != ProtocolHeadLength || danmuHead.Action != 8)
87 | {
88 | //如果头信息的长度不是16,或者Action的的值不是8 (服务器接受认证包后回应的第一个数据)
89 | //这是错误处理的代码
90 | return false;
91 | }
92 |
93 | var dataBuffer = new byte[danmuHead.PacketLength - danmuHead.HeaderLength];
94 | await _roomStream.ReadAsync(dataBuffer, 0, danmuHead.MessageLength());
95 | var s = Encoding.Default.GetString(dataBuffer);
96 | var data = JObject.Parse(s);
97 | if (int.Parse(data["code"].ToString()) != 0)
98 | {
99 | return false;
100 | }
101 |
102 | _connected = true;
103 | //循环发送心跳信息
104 | #pragma warning disable 4014
105 | SendHeartbeatLoop();
106 | #pragma warning restore 4014
107 | return true;
108 | }
109 |
110 | ///
111 | /// 循环读取消息,禁止重复调用
112 | ///
113 | ///
114 | public async Task ReadMessageLoop()
115 | {
116 | while (_connected)
117 | {
118 | var headBuffer = new byte[ProtocolHeadLength];
119 | //先读取一次头信息
120 | await _roomStream.ReadAsync(headBuffer, 0, ProtocolHeadLength);
121 | //解析头信息
122 | DanmuHead danmuHead = DanmuHead.BufferToDanmuHead(headBuffer);
123 | //判断协议
124 | if (danmuHead.HeaderLength != ProtocolHeadLength)
125 | {
126 | continue;
127 | }
128 |
129 | //初始化一个放数据的byte数组
130 | byte[] dataBuffer;
131 | if (danmuHead.Action == 3)
132 | {
133 | //给服务器发送心跳信息后的回应信息,所带的数据是直播间的观看人数(人气值)
134 | dataBuffer = new byte[danmuHead.MessageLength()];
135 | await _roomStream.ReadAsync(dataBuffer, 0, danmuHead.MessageLength());
136 | var audiences = EndianBitConverter.EndianBitConverter.BigEndian.ToInt32(dataBuffer, 0);
137 | _messageHandler.AudiencesHandlerAsync(audiences);
138 | continue;
139 | }
140 |
141 | string tmpData;
142 | JObject json = null;
143 | if (danmuHead.Action == 5 && danmuHead.Version == ProtocolVersion)
144 | {
145 | //有效负载为礼物、弹幕、公告等内容数据
146 | //读取数据放入缓冲区
147 | dataBuffer = new byte[danmuHead.MessageLength()];
148 | await _roomStream.ReadAsync(dataBuffer, 0, danmuHead.MessageLength());
149 | //之后把数据放入到内存流
150 | string jsonStr;
151 | using (var ms = new MemoryStream(dataBuffer, 2, danmuHead.MessageLength() - 2))
152 | {
153 | //使用内存流生成解压流(压缩流)
154 | var deflate = new DeflateStream(ms, CompressionMode.Decompress);
155 | var headerbuffer = new byte[ProtocolHeadLength];
156 | try
157 | {
158 | while (true)
159 | {
160 | await deflate.ReadAsync(headerbuffer, 0, ProtocolHeadLength);
161 | danmuHead = DanmuHead.BufferToDanmuHead(headerbuffer);
162 | var messageBuffer = new byte[danmuHead.MessageLength()];
163 | var readLength = await deflate.ReadAsync(messageBuffer, 0, danmuHead.MessageLength());
164 | jsonStr = Encoding.UTF8.GetString(messageBuffer, 0, danmuHead.MessageLength());
165 | if (readLength == 0)
166 | {
167 | break;
168 | }
169 | json = JObject.Parse(jsonStr);
170 | _messageDispatcher.DispatchAsync(json, _messageHandler);
171 | }
172 | continue;
173 | }
174 | catch (Exception e)
175 | {
176 | //读数据超出长度
177 | Debug.WriteLine(e);
178 | throw;
179 | }
180 | }
181 | }
182 |
183 | dataBuffer = new byte[danmuHead.MessageLength()];
184 | await _roomStream.ReadAsync(dataBuffer, 0, danmuHead.MessageLength());
185 | tmpData = Encoding.UTF8.GetString(dataBuffer);
186 | try
187 | {
188 | json = JObject.Parse(tmpData);
189 | }
190 | catch (Exception e)
191 | {
192 | Debug.WriteLine(e);
193 | throw e;
194 | }
195 | if (!"DANMU_MSG".Equals(json["cmd"].ToString()) && !"SEND_GIFT".Equals(json["cmd"].ToString()))
196 | {
197 | _messageDispatcher.DispatchAsync(json, _messageHandler);
198 | }
199 | }
200 | }
201 |
202 | ///
203 | /// 发送加入房间的消息
204 | ///
205 | ///
206 | ///
207 | public async Task SendJoinMsgAsync(string token)
208 | {
209 | var packageModel = new Dictionary
210 | {
211 | {"roomid", _roomId},
212 | {"uid", 0},
213 | {"protover", ProtocolVersion},
214 | {"token", token},
215 | {"platform", "web"},
216 | {"type", 2}
217 | };
218 | var body = JsonConvert.SerializeObject(packageModel);
219 | await SendSocketDataAsync(7, body);
220 | return true;
221 | }
222 |
223 | ///
224 | /// 发送消息的方法
225 | ///
226 | ///
227 | ///
228 | ///
229 | public Task SendSocketDataAsync(int action, string body)
230 | {
231 | return SendSocketDataAsync(ProtocolHeadLength, ProtocolVersion, action, 1, body);
232 | }
233 |
234 | ///
235 | /// 发送消息的方法
236 | ///
237 | ///
238 | ///
239 | ///
240 | ///
241 | ///
242 | ///
243 | public async Task SendSocketDataAsync(short headLength, short version, int action, int param,
244 | string body)
245 | {
246 | var data = Encoding.UTF8.GetBytes(body);
247 | var packageLength = data.Length + headLength;
248 |
249 | var buffer = new byte[packageLength];
250 | var ms = new MemoryStream(buffer);
251 | await ms.WriteAsync(EndianBitConverter.EndianBitConverter.BigEndian.GetBytes(buffer.Length), 0, 4);
252 | await ms.WriteAsync(EndianBitConverter.EndianBitConverter.BigEndian.GetBytes(headLength), 0, 2);
253 | await ms.WriteAsync(EndianBitConverter.EndianBitConverter.BigEndian.GetBytes(version), 0, 2);
254 | await ms.WriteAsync(EndianBitConverter.EndianBitConverter.BigEndian.GetBytes(action), 0, 4);
255 | await ms.WriteAsync(EndianBitConverter.EndianBitConverter.BigEndian.GetBytes(param), 0, 4);
256 | if (data.Length > 0)
257 | {
258 | await ms.WriteAsync(data, 0, data.Length);
259 | }
260 |
261 | await _roomStream.WriteAsync(buffer, 0, buffer.Length);
262 | }
263 |
264 | ///
265 | /// 循环发送心跳,禁止重复调用
266 | ///
267 | ///
268 | ///
269 | private async Task SendHeartbeatLoop()
270 | {
271 | try
272 | {
273 | _connected = _tcpClient.Connected;
274 |
275 | while (_connected)
276 | {
277 | try
278 | {
279 | await SendSocketDataAsync(ProtocolHeadLength, ProtocolVersion, 2, 1, "");
280 | //休眠30秒
281 | await Task.Delay(30000);
282 | }
283 | catch (Exception e)
284 | {
285 | _connected = false;
286 | Console.WriteLine("发送心跳失败");
287 | throw e;
288 | }
289 | }
290 | }
291 | catch (Exception e)
292 | {
293 | Disconnect();
294 | throw e;
295 | }
296 | }
297 |
298 | ///
299 | /// 关闭连接的方法
300 | ///
301 | ///
302 | public void Disconnect()
303 | {
304 | try
305 | {
306 | _connected = false;
307 | _tcpClient.Dispose();
308 | _roomStream = null;
309 | }
310 | catch (Exception e)
311 | {
312 | //错误处理
313 | throw e;
314 | }
315 | }
316 |
317 | ///
318 | /// 反回连接的状态
319 | ///
320 | ///
321 | public bool Connected()
322 | {
323 | return _connected;
324 | }
325 | }
326 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/BaseMessage.cs:
--------------------------------------------------------------------------------
1 | namespace BilibiliUtilities.Live.Message
2 | {
3 | public class BaseMessage
4 | {
5 | ///
6 | /// 原始数据 json字符串
7 | ///
8 | public string Metadata;
9 |
10 | }
11 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/ComboEndMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | // TODO 还需要收集数据
8 | public class ComboEndMessage:BaseMessage
9 | {
10 | ///
11 | /// 用户UID
12 | ///
13 | public long UserId;
14 |
15 | ///
16 | /// 用户名称
17 | ///
18 | public string Username;
19 |
20 | ///
21 | /// 连续送礼 数量
22 | ///
23 | public int ComboNum;
24 |
25 | ///
26 | /// 礼物数量
27 | ///
28 | public int GiftNum;
29 |
30 | ///
31 | /// 未知
32 | ///
33 | public int BatchComboNum;
34 |
35 | ///
36 | /// 价格,即是银瓜子也是金瓜子
37 | ///
38 | public int Price;
39 |
40 | ///
41 | /// 礼物名称
42 | ///
43 | public string GiftName;
44 |
45 | ///
46 | /// 礼物ID
47 | ///
48 | public int GiftId;
49 |
50 | ///
51 | /// 舰长等级?
52 | ///
53 | public int GuardLevel;
54 |
55 | public static ComboEndMessage JsonToComboEndMessage(JObject json)
56 | {
57 | if (!"COMBO_SEND".Equals(json["cmd"].ToString()))
58 | {
59 | throw new ArgumentException("'cmd' 的值不是 'COMBO_END'");
60 | }
61 |
62 | var data = json["data"];
63 | return new ComboEndMessage
64 | {
65 | UserId = long.Parse(data["uid"].ToString()),
66 | Username = data["uname"]?.ToString(),
67 | ComboNum = int.Parse(data["combo_num"].ToString()),
68 | GiftNum = int.Parse(data["gift_num"].ToString()),
69 | BatchComboNum = int.Parse(data["gift_num"].ToString()),
70 | Price = int.Parse(data["gift_num"].ToString()),
71 | GiftName = data["gift_name"].ToString(),
72 | GiftId = int.Parse(data["gift_id"].ToString()),
73 | Metadata = JsonConvert.SerializeObject(json)
74 | };
75 | }
76 |
77 | public static ComboEndMessage JsonToComboEndMessage(string jsonStr)
78 | {
79 | try
80 | {
81 | var json = JObject.Parse(jsonStr);
82 | return JsonToComboEndMessage(json);
83 | }
84 | catch (JsonReaderException)
85 | {
86 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
87 | }
88 | }
89 | }
90 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/DanmuMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.Linq;
4 | using Newtonsoft.Json;
5 | using Newtonsoft.Json.Linq;
6 |
7 | namespace BilibiliUtilities.Live.Message
8 | {
9 | public class DanmuMessage : BaseMessage
10 | {
11 | ///
12 | /// 用户UID
13 | ///
14 | public long UserId;
15 |
16 | ///
17 | /// 用户名称
18 | ///
19 | public string Username;
20 |
21 | ///
22 | /// 弹幕内容
23 | ///
24 | public string Content;
25 |
26 | ///
27 | /// 勋章名称
28 | ///
29 | public string Medal;
30 |
31 | ///
32 | /// 勋章等级
33 | ///
34 | public int MedalLevel;
35 |
36 | ///
37 | /// 勋章所有者
38 | ///
39 | public string MedalOwnerName;
40 |
41 | ///
42 | /// 舰队等级
43 | /// 0 为非船员 1 为总督 2 为提督 3 为舰长
44 | ///
45 | public int UserGuardLevel;
46 | ///
47 | /// 是不是房管
48 | ///
49 | public bool Admin;
50 | ///
51 | /// 是不是老爷
52 | ///
53 | public bool Vip;
54 | public DanmuMessage()
55 | {
56 | }
57 |
58 | public static DanmuMessage JsonToDanmuMessage(JObject json)
59 | {
60 | if (!"DANMU_MSG".Equals(json["cmd"].ToString()))
61 | {
62 | throw new ArgumentException("'cmd' 的值不是 'DANMU_MSG'");
63 | }
64 |
65 | var info = json["info"];
66 | try
67 | {
68 | var medal = "";
69 | var medalLevel = 0;
70 | var medalOwnerName = "";
71 | //判断有没有佩戴粉丝勋章
72 | if (info[3].ToArray().Length != 0)
73 | {
74 | medal = info[3][1].ToString();
75 | medalLevel = int.Parse(info[3][0].ToString());
76 | medalOwnerName = info[3][2].ToString();
77 | }
78 |
79 | return new DanmuMessage
80 | {
81 | UserId = long.Parse(info[2][0].ToString()),
82 | Username = info[2][1].ToString(),
83 | Content = info[1].ToString(),
84 | Medal = medal,
85 | MedalLevel = medalLevel,
86 | MedalOwnerName = medalOwnerName,
87 | Admin = info[2][2].ToString().Equals("1"),
88 | Vip = info[2][3].ToString().Equals("1"),
89 | UserGuardLevel = int.Parse(info[7].ToString()),
90 | Metadata = JsonConvert.SerializeObject(json)
91 | };
92 | }
93 | catch (ArgumentOutOfRangeException e)
94 | {
95 | Debug.WriteLine(e);
96 | throw;
97 | }
98 | }
99 |
100 | public static DanmuMessage JsonToDanmuMessage(string jsonStr)
101 | {
102 | try
103 | {
104 | var json = JObject.Parse(jsonStr);
105 | return JsonToDanmuMessage(json);
106 | }
107 | catch (JsonReaderException)
108 | {
109 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
110 | }
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/EntryEffectMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class EntryEffectMessage : BaseMessage
8 | {
9 | ///
10 | /// [未知]既不是用户ID也不是房间ID
11 | ///
12 | public int Id;
13 |
14 | ///
15 | /// 用户ID
16 | ///
17 | public long UserId;
18 |
19 | ///
20 | /// [未知]TargetId
21 | ///
22 | public long TargetId;
23 |
24 | ///
25 | /// [未知]MockEffect
26 | ///
27 | public int MockEffect;
28 |
29 | ///
30 | /// 头像的URL
31 | ///
32 | public string FaceUrl;
33 |
34 | ///
35 | /// 舰队等级
36 | /// 0 为非船员 1 为总督 2 为提督 3 为舰长
37 | ///
38 | public int PrivilegeType;
39 |
40 | ///
41 | /// 显示出来的通知,如下
42 | /// 欢迎舰长 <%一个不太好看的用户名%> 进入直播间
43 | ///
44 | public string CopyWriting;
45 |
46 | public static EntryEffectMessage JsonToEntryEffectMessage(JObject json)
47 | {
48 | if (!"ENTRY_EFFECT".Equals(json["cmd"].ToString()))
49 | {
50 | throw new ArgumentException("'cmd' 的值不是 'ENTRY_EFFECT'");
51 | }
52 |
53 | var data = json["data"];
54 | return new EntryEffectMessage
55 | {
56 | Id = int.Parse(data["uid"].ToString()),
57 | UserId = long.Parse(data["uid"].ToString()),
58 | TargetId = long.Parse(data["target_id"].ToString()),
59 | MockEffect = int.Parse(data["mock_effect"].ToString()),
60 | FaceUrl = data["face"].ToString(),
61 | PrivilegeType = int.Parse(data["privilege_type"].ToString()),
62 | CopyWriting = data["copy_writing"].ToString(),
63 | Metadata = JsonConvert.SerializeObject(json)
64 | };
65 | }
66 |
67 | public static EntryEffectMessage JsonToEntryEffectMessage(string jsonStr)
68 | {
69 | try
70 | {
71 | var json = JObject.Parse(jsonStr);
72 | return JsonToEntryEffectMessage(json);
73 | }
74 | catch (JsonReaderException)
75 | {
76 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
77 | }
78 | }
79 | }
80 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/GiftMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class GiftMessage:BaseMessage
8 | {
9 | ///
10 | /// 礼物名称
11 | ///
12 | public string GiftName;
13 |
14 | ///
15 | /// 礼物ID
16 | ///
17 | public int GiftId;
18 |
19 | ///
20 | /// 礼物数量
21 | ///
22 | public int GiftNum;
23 |
24 | ///
25 | /// 送礼物的用户名
26 | ///
27 | public string Username;
28 |
29 | ///
30 | /// 用户ID
31 | ///
32 | public long UserId;
33 |
34 | ///
35 | /// 头像的URL
36 | ///
37 | public string FaceUrl;
38 |
39 | ///
40 | /// 礼物类型,
41 | /// TODO 没收集类型
42 | ///
43 | public string GiftType;
44 |
45 | ///
46 | /// 礼物单价
47 | ///
48 | public long Price;
49 |
50 | ///
51 | /// 瓜子类型
52 | /// gold是金瓜子
53 | /// silver是银瓜子
54 | ///
55 | public string CoinType;
56 |
57 | ///
58 | /// 总价值
59 | ///
60 | public long TotalCoin;
61 |
62 | public static GiftMessage JsonToGiftMessage(JObject json)
63 | {
64 | if (!"SEND_GIFT".Equals(json["cmd"].ToString()))
65 | {
66 | throw new ArgumentException("字段 'cmd' 的值不是 'SEND_GIFT'");
67 | }
68 | var data = json["data"];
69 | return new GiftMessage
70 | {
71 | GiftName = data["giftName"] + "",
72 | GiftId = int.Parse(data["giftId"] + ""),
73 | GiftNum = int.Parse(data["num"] + ""),
74 | Username = data["uname"] + "",
75 | UserId = long.Parse(data["uid"] + ""),
76 | FaceUrl = data["face"] + "",
77 | GiftType = data["giftType"] + "",
78 | Price = long.Parse(data["price"] + ""),
79 | TotalCoin = long.Parse(data["total_coin"] + ""),
80 | CoinType = data["coin_type"] + "",
81 | Metadata = JsonConvert.SerializeObject(json)
82 | };
83 | }
84 |
85 | public static GiftMessage JsonToGiftMessage(string jsonStr)
86 | {
87 | try
88 | {
89 | var json = JObject.Parse(jsonStr);
90 | return JsonToGiftMessage(json);
91 | }
92 | catch (JsonReaderException)
93 | {
94 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
95 | }
96 | }
97 | }
98 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/GuardBuyMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | ///
8 | /// 相对UserToastMessage,这个信息较少且不准确,建议使用UserToastMessage
9 | ///
10 | public class GuardBuyMessage : BaseMessage
11 | {
12 | ///
13 | /// 用户UID
14 | ///
15 | public long UserId;
16 |
17 | ///
18 | /// 用户名称
19 | ///
20 | public string Username;
21 |
22 | ///
23 | /// 数量
24 | ///
25 | public int Num;
26 |
27 | ///
28 | /// 舰队等级
29 | /// 0 为非船员 1 为总督 2 为提督 3 为舰长
30 | ///
31 | public int GuardLevel;
32 |
33 | ///
34 | /// 价格,不打折的金瓜子数量
35 | /// 比如舰长原价是198R,但是后续续费是138R,那么Price还是198000金瓜子
36 | ///
37 | public int Price;
38 |
39 | ///
40 | /// RoleName
41 | /// 其实只会是数字,但是不知道为什么B站给的信息是"Name",而不是"ID"
42 | ///
43 | public string RoleName;
44 |
45 | ///
46 | /// 礼物名称,舰长 提督 总督
47 | ///
48 | public string GiftName;
49 |
50 | public static GuardBuyMessage JsonToGuardBuyMessage(JObject json)
51 | {
52 | if (!"GUARD_BUY".Equals(json["cmd"].ToString()))
53 | {
54 | throw new ArgumentException("'cmd' 的值不是 'GUARD_BUY'");
55 | }
56 |
57 | var data = json["data"];
58 | return new GuardBuyMessage
59 | {
60 | UserId = long.Parse(data["uid"].ToString()),
61 | Username = data["username"].ToString(),
62 | GuardLevel = int.Parse(data["guard_level"].ToString()),
63 | Num = int.Parse(data["num"].ToString()),
64 | Price = int.Parse(data["price"].ToString()),
65 | RoleName = data["role_name"].ToString(),
66 | GiftName = data["gift_name"].ToString(),
67 | Metadata = JsonConvert.SerializeObject(json)
68 | };
69 | }
70 |
71 | public static GuardBuyMessage JsonToGuardBuyMessage(string jsonStr)
72 | {
73 | try
74 | {
75 | var json = JObject.Parse(jsonStr);
76 | return JsonToGuardBuyMessage(json);
77 | }
78 | catch (JsonReaderException)
79 | {
80 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
81 | }
82 | }
83 | }
84 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/InteractWordMessage.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Linq;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace BilibiliUtilities.Live.Message
10 | {
11 | public class InteractWordMessage : BaseMessage
12 | {
13 |
14 | ///
15 | /// 用户UID
16 | ///
17 | public long UserId;
18 |
19 | ///
20 | /// 用户名称
21 | ///
22 | public string Username;
23 |
24 | ///
25 | /// 勋章名称
26 | ///
27 | public string Medal;
28 |
29 | ///
30 | /// 勋章等级
31 | ///
32 | public int MedalLevel;
33 |
34 | ///
35 | /// 勋章所有者
36 | ///
37 | public long MedalOwnerId;
38 |
39 | public static InteractWordMessage JsonToInteractWordMessage(JObject json)
40 | {
41 | if (!"INTERACT_WORD".Equals(json["cmd"].ToString()))
42 | {
43 | throw new ArgumentException("'cmd' 的值不是 'INTERACT_WORD'");
44 | }
45 |
46 | var data = json["data"];
47 | return new InteractWordMessage
48 | {
49 | UserId = long.Parse(data["uid"].ToString()),
50 | Username = data["uname"]?.ToString(),
51 | Medal = data["fans_medal"]["medal_name"]?.ToString(),
52 | MedalLevel = int.Parse(data["fans_medal"]["medal_level"]?.ToString()),
53 | MedalOwnerId = long.Parse(data["fans_medal"]["target_id"].ToString()),
54 | Metadata = JsonConvert.SerializeObject(json)
55 | };
56 | }
57 |
58 | public static InteractWordMessage JsonToInteractWordMessage(string jsonStr)
59 | {
60 | try
61 | {
62 | var json = JObject.Parse(jsonStr);
63 | return JsonToInteractWordMessage(json);
64 | }
65 | catch (JsonReaderException)
66 | {
67 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
68 | }
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/NoticeMessage.cs:
--------------------------------------------------------------------------------
1 | namespace BilibiliUtilities.Live.Message
2 | {
3 | public class NoticeMessage:BaseMessage
4 | {
5 | //通知消息,暂时不处理
6 | }
7 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/RoomChangeMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class RoomChangeMessage : BaseMessage
8 | {
9 | ///
10 | /// 标题
11 | ///
12 | public string Title;
13 |
14 | ///
15 | /// 子分区ID
16 | ///
17 | public int AreaId;
18 |
19 | ///
20 | /// 分区ID
21 | ///
22 | public int ParentAreaId;
23 |
24 | ///
25 | /// 子分区名称
26 | ///
27 | public string AreaName;
28 |
29 | ///
30 | /// 分区名称
31 | ///
32 | public string ParentAreaName;
33 |
34 | public static RoomChangeMessage JsonToRoomChangeMessage(JObject json)
35 | {
36 | return new RoomChangeMessage
37 | {
38 | Title = json["data"]["title"].ToString(),
39 | AreaId = int.Parse(json["data"]["area_id"].ToString()),
40 | ParentAreaId = int.Parse(json["data"]["parent_area_id"].ToString()),
41 | AreaName = json["data"]["area_name"].ToString(),
42 | ParentAreaName = json["data"]["parent_area_name"].ToString(),
43 | Metadata = JsonConvert.SerializeObject(json)
44 | };
45 | }
46 |
47 | public static RoomChangeMessage JsonToRoomChangeMessage(string jsonStr)
48 | {
49 | try
50 | {
51 | return JsonToRoomChangeMessage(JObject.Parse(jsonStr));
52 | }
53 | catch (JsonReaderException)
54 | {
55 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
56 | }
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/RoomRankMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class RoomRankMessage : BaseMessage
8 | {
9 | ///
10 | ///
11 | ///
12 | public int RoomId;
13 |
14 | ///
15 | ///
16 | ///
17 | public string RankDesc;
18 |
19 | ///
20 | ///
21 | ///
22 | public string Color;
23 |
24 | ///
25 | /// 给手机端用的URL
26 | ///
27 | public string H5Url;
28 |
29 | ///
30 | /// 给电脑端用的URL
31 | ///
32 | public string WebUrl;
33 |
34 | ///
35 | /// 排名信息的发布时间
36 | ///
37 | public DateTime Time;
38 |
39 | public static RoomRankMessage JsonToRoomRankMessage(JObject json)
40 | {
41 | return new RoomRankMessage
42 | {
43 | RoomId = int.Parse(json["data"]["roomid"].ToString()),
44 | RankDesc = json["data"]["rank_desc"].ToString(),
45 | Color = json["data"]["red_notice"].ToString(),
46 | H5Url = json["data"]["h5_url"].ToString(),
47 | WebUrl = json["data"]["web_url"].ToString(),
48 | // Time = ,
49 | Metadata = JsonConvert.SerializeObject(json)
50 | };
51 | }
52 |
53 | public static RoomRankMessage JsonToRoomRankMessage(string jsonStr)
54 | {
55 | try
56 | {
57 | return JsonToRoomRankMessage(JObject.Parse(jsonStr));
58 | }
59 | catch (JsonReaderException)
60 | {
61 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
62 | }
63 | }
64 | }
65 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/RoomUpdateMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class RoomUpdateMessage:BaseMessage
8 | {
9 | ///
10 | /// 真正的房间号
11 | ///
12 | public int RoomId;
13 |
14 | ///
15 | /// 主播当前的粉丝数量
16 | ///
17 | public int Fans;
18 |
19 | ///
20 | /// 红色通知?
21 | ///
22 | public int RedNotice;
23 |
24 | public static RoomUpdateMessage JsonToRoomUpdateMessage(JObject json)
25 | {
26 | return new RoomUpdateMessage
27 | {
28 | RoomId = int.Parse(json["data"]["roomid"].ToString()),
29 | Fans = int.Parse(json["data"]["fans"].ToString()),
30 | RedNotice = int.Parse(json["data"]["red_notice"].ToString()),
31 | Metadata = JsonConvert.SerializeObject(json)
32 | };
33 | }
34 | public static RoomUpdateMessage JsonToRoomUpdateMessage(string jsonStr)
35 | {
36 | try
37 | {
38 | return JsonToRoomUpdateMessage(JObject.Parse(jsonStr));
39 | }
40 | catch (JsonReaderException)
41 | {
42 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
43 | }
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/UserToastMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | ///
8 | /// 相对GuardBuyMessage,这个信息更为全面且准确,建议使用这个
9 | ///
10 | public class UserToastMessage : BaseMessage
11 | {
12 | ///
13 | /// [未知]操作类型
14 | /// 可能代表着续费之类的
15 | ///
16 | public int OpType;
17 |
18 | ///
19 | /// 用户UID
20 | ///
21 | public long UserId;
22 |
23 | ///
24 | /// 用户名称
25 | ///
26 | public string Username;
27 |
28 | ///
29 | /// 数量
30 | ///
31 | public int Num;
32 |
33 | ///
34 | /// 舰队等级
35 | /// 0 为非船员 1 为总督 2 为提督 3 为舰长
36 | ///
37 | public int GuardLevel;
38 |
39 | ///
40 | /// 价格,实际花费的价格
41 | /// 比如舰长原价是198R,但是后续续费是158R,那么Price就是158000金瓜子
42 | ///
43 | public int Price;
44 |
45 | ///
46 | /// 有效值:舰长 提督 总督
47 | /// 这里会显示名称,而不再是数字
48 | ///
49 | public string RoleName;
50 |
51 | ///
52 | /// 单位,月
53 | ///
54 | public string Unit;
55 |
56 | ///
57 | /// 当前大航海人数有多少
58 | ///
59 | public int GuardCount;
60 |
61 | public string ToastMsg;
62 |
63 | public static UserToastMessage JsonToUserToastMessage(JObject json)
64 | {
65 | if (!"USER_TOAST_MSG".Equals(json["cmd"].ToString()))
66 | {
67 | throw new ArgumentException("'cmd' 的值不是 'USER_TOAST_MSG'");
68 | }
69 |
70 | var data = json["data"];
71 | return new UserToastMessage
72 | {
73 | OpType = int.Parse(data["op_type"].ToString()),
74 | UserId = long.Parse(data["uid"].ToString()),
75 | Username = data["username"].ToString(),
76 | Num = int.Parse(data["num"].ToString()),
77 | GuardLevel = int.Parse(data["guard_level"].ToString()),
78 | Price = int.Parse(data["price"].ToString()),
79 | RoleName = data["role_name"].ToString(),
80 | Unit = data["unit"].ToString(),
81 | ToastMsg = data["toast_msg"].ToString(),
82 | Metadata = JsonConvert.SerializeObject(json)
83 | };
84 | }
85 |
86 | public static UserToastMessage JsonToUserToastMessage(string jsonStr)
87 | {
88 | try
89 | {
90 | var json = JObject.Parse(jsonStr);
91 | return JsonToUserToastMessage(json);
92 | }
93 | catch (JsonReaderException)
94 | {
95 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
96 | }
97 | }
98 | }
99 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/WelcomeGuardMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class WelcomeGuardMessage : BaseMessage
8 | {
9 | ///
10 | ///
11 | ///
12 | public long UserId;
13 |
14 | ///
15 | ///
16 | ///
17 | public string Username;
18 |
19 | ///
20 | ///
21 | ///
22 | public int GuardLevel;
23 |
24 | ///
25 | ///
26 | ///
27 | public int MockEffect;
28 |
29 | public static WelcomeGuardMessage JsonToWelcomeGuardMessage(JObject json)
30 | {
31 | return new WelcomeGuardMessage
32 | {
33 | UserId = long.Parse(json["data"]["uid"].ToString()),
34 | Username = json["data"]["username"].ToString(),
35 | GuardLevel = int.Parse(json["data"]["guard_level"].ToString()),
36 | MockEffect = int.Parse(json["data"]["mock_effect"].ToString()),
37 | Metadata = JsonConvert.SerializeObject(json)
38 | };
39 | }
40 |
41 | public static WelcomeGuardMessage JsonToWelcomeGuardMessage(string jsonStr)
42 | {
43 | try
44 | {
45 | var json = JObject.Parse(jsonStr);
46 | return JsonToWelcomeGuardMessage(json);
47 | }
48 | catch (JsonReaderException)
49 | {
50 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Live/Message/WelcomeMessage.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Newtonsoft.Json;
3 | using Newtonsoft.Json.Linq;
4 |
5 | namespace BilibiliUtilities.Live.Message
6 | {
7 | public class WelcomeMessage:BaseMessage
8 | {
9 | ///
10 | /// 用户ID
11 | ///
12 | public long UserId;
13 |
14 | ///
15 | /// 用户名
16 | ///
17 | public string Username;
18 |
19 | ///
20 | /// 是不是管理员?
21 | ///
22 | public bool Admin;
23 |
24 | ///
25 | /// Svip?
26 | ///
27 | public int Svip;
28 |
29 | ///
30 | /// Vip?
31 | ///
32 | public int Vip;
33 |
34 | ///
35 | /// 未知参数
36 | ///
37 | public int MockEffect;
38 |
39 | /*
40 | {
41 | "cmd": "WELCOME",
42 | "data": {
43 | "uid": 86686683,
44 | "uname": "影之余烬",
45 | "is_admin": false,
46 | "svip": 0,
47 | "vip": 1,
48 | "mock_effect": 0
49 | }
50 | }
51 | */
52 |
53 | public static WelcomeMessage JsonToWelcomeMessage(JObject json)
54 | {
55 | if (!"WELCOME".Equals(json["cmd"].ToString()))
56 | {
57 | throw new ArgumentException("'cmd' 的值不是 'WELCOME'");
58 | }
59 |
60 | var data = json["data"];
61 | return new WelcomeMessage
62 | {
63 | UserId = long.Parse(data["uid"].ToString()),
64 | Username = data["uname"].ToString(),
65 | Admin = bool.Parse(data["is_admin"].ToString()),
66 | Svip = int.Parse(data["svip"].ToString()),
67 | Vip = int.Parse(data["vip"].ToString()),
68 | MockEffect = int.Parse(data["mock_effect"].ToString()),
69 | Metadata = JsonConvert.SerializeObject(json)
70 | };
71 | }
72 |
73 | public static WelcomeMessage JsonToWelcomeMessage(string jsonStr)
74 | {
75 | try
76 | {
77 | var json = JObject.Parse(jsonStr);
78 | return JsonToWelcomeMessage(json);
79 | }
80 | catch (JsonReaderException)
81 | {
82 | throw new AggregateException("JSON字符串没有成功转换成Json对象");
83 | }
84 | }
85 | }
86 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Test/BilibiliUtilities.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | netcoreapp3.1
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Test/LiveLib/LiveHandler.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using BilibiliUtilities.Live.Lib;
4 | using BilibiliUtilities.Live.Message;
5 | namespace BilibiliUtilities.Test.LiveLib
6 | {
7 | public class LiveHandler:IMessageHandler
8 | {
9 | //可以放置自己的参数用来使用,比如WPF的window对象
10 | public bool Param;
11 |
12 |
13 | public async Task DanmuMessageHandlerAsync(DanmuMessage danmuMessage)
14 | {
15 |
16 | Console.WriteLine($"发送者:{danmuMessage.Username},内容:{danmuMessage.Content}");
17 | }
18 |
19 | public async Task AudiencesHandlerAsync(int audiences)
20 | {
21 | Console.WriteLine($"当前人气值:{audiences}");
22 | }
23 |
24 | public async Task NoticeMessageHandlerAsync(NoticeMessage noticeMessage)
25 | {
26 | Console.WriteLine("通知信息未处理");
27 | }
28 |
29 | public async Task GiftMessageHandlerAsync(GiftMessage giftMessage)
30 | {
31 | //如果礼物不是辣条
32 | if (giftMessage.GiftId!=1)
33 | {
34 | Console.WriteLine($"{giftMessage.Username}送出了{giftMessage.GiftNum}个{giftMessage.GiftName},价值:{giftMessage.TotalCoin}个{giftMessage.CoinType}");
35 | }
36 | }
37 |
38 | public async Task WelcomeMessageHandlerAsync(WelcomeMessage welcomeMessage)
39 | {
40 | Console.WriteLine($"欢迎{welcomeMessage.Username}进入直播间");
41 | }
42 |
43 | public async Task ComboEndMessageHandlerAsync(ComboEndMessage comboEndMessage)
44 | {
45 | Console.WriteLine($"{comboEndMessage.Username}的{comboEndMessage.GiftName}连击结束了,送出了{comboEndMessage.ComboNum}个,总价值{comboEndMessage.Price}个金瓜子");
46 | }
47 |
48 | public async Task RoomUpdateMessageHandlerAsync(RoomUpdateMessage roomUpdateMessage)
49 | {
50 | Console.WriteLine($"UP当前粉丝数量{roomUpdateMessage.Fans}");
51 | }
52 |
53 | public async Task WelcomeGuardMessageHandlerAsync(WelcomeGuardMessage welcomeGuardMessage)
54 | {
55 | Console.WriteLine($"房管{welcomeGuardMessage.Username}进入直播间");
56 | }
57 |
58 | public async Task LiveStartMessageHandlerAsync(int roomId)
59 | {
60 | Console.WriteLine("直播开始");
61 | }
62 |
63 | public async Task LiveStopMessageHandlerAsync(int roomId)
64 | {
65 | Console.WriteLine("直播关闭");
66 | }
67 |
68 | public async Task EntryEffectMessageHandlerAsync(EntryEffectMessage entryEffectMessage)
69 | {
70 | Console.WriteLine($"⚡⚡⚡<特效>⚡⚡⚡{entryEffectMessage.CopyWriting}⚡⚡⚡<特效>⚡⚡⚡");
71 | }
72 |
73 | public async Task GuardBuyMessageHandlerAsync(GuardBuyMessage guardBuyMessage)
74 | {
75 | Console.WriteLine($"{guardBuyMessage.Username}购买了{guardBuyMessage.Num}月的{guardBuyMessage.GiftName}");
76 | }
77 |
78 | public async Task UserToastMessageHandlerAsync(UserToastMessage userToastMessage)
79 | {
80 | Console.WriteLine($"{userToastMessage.Username}购买了{userToastMessage.Num}{userToastMessage.Unit}的{userToastMessage.RoleName}");
81 | }
82 |
83 | public async Task InteractWordMessageHandlerAsync(InteractWordMessage message)
84 | {
85 | if (!string.IsNullOrEmpty(message.Medal))
86 | Console.WriteLine($"{message.Medal}.{message.MedalLevel} {message.Username} 进入直播间");
87 | else
88 | Console.WriteLine($"{message.Username} 进入直播间");
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Test/Tests/LiveTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using BilibiliUtilities.Live;
3 | using Xunit;
4 |
5 | namespace BilibiliUtilities.Test.Tests
6 | {
7 | public class LiveTest
8 | {
9 |
10 | [Fact]
11 | public static void TestLive()
12 | {
13 | Console.WriteLine("直播间房间号:");
14 | Start();
15 | Console.ReadLine();
16 | }
17 |
18 | public static async void Start()
19 | {
20 | var liveHandler = new LiveLib.LiveHandler();
21 | var sroomid = Console.ReadLine();
22 | var roomid = int.Parse(sroomid);
23 | //第一个参数是直播间的房间号
24 | //第二个参数是自己实现的处理器
25 | //第三个参数是可选的,可以是默认的消息分发器,也可以是自己实现的消息分发器
26 | LiveRoom room = new LiveRoom(roomid, liveHandler);
27 | //等待连接,该方法会反回是否连接成功
28 | //或者使用room.Connected,该属性会反馈连接状态
29 | if (!await room.ConnectAsync())
30 | {
31 | Console.WriteLine("连接失败");
32 | return;
33 | }
34 | Console.WriteLine("连接成功");
35 | Console.WriteLine("按回车结束");
36 | //消息由Dispatcher分发到对应的MessageHandler中对应方法
37 | await room.ReadMessageLoop();
38 | }
39 | }
40 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Test/Tests/VideoTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using BilibiliUtilities.Utils.CentralUtils;
3 | using Xunit;
4 |
5 | namespace BilibiliUtilities.Test.Tests
6 | {
7 | public class VideoTest
8 | {
9 | [Fact]
10 | public static async void TestAvToBv()
11 | {
12 | var BvId = await VideoUtil.AvToBv(170001);
13 | Console.WriteLine(BvId);
14 | }
15 |
16 | [Fact]
17 | public static async void TestBvToAv()
18 | {
19 | var AvId = await VideoUtil.BvToAv("BV17x411w7KC");
20 | Console.WriteLine(AvId);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Utils/BilibiliUtilities - Backup.Utils.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | 0.0.2
6 | BilibiliUtilities-Utils
7 | Basic functional support for bilibilibiliutilities and users
8 | https://github.com/is-a-gamer/BilibiliUtilities
9 | Bilibili
10 | 8
11 | netcoreapp3.1
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Utils/BilibiliUtilities.Utils.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | 0.0.2
6 | BilibiliUtilities-Utils
7 | Basic functional support for bilibilibiliutilities and users
8 | https://github.com/is-a-gamer/BilibiliUtilities
9 | Bilibili
10 | 7.3
11 | true
12 | 0.03
13 | net471;netstandard2.0;netcoreapp3.1
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Utils/CentralUtils/VideoUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Threading.Tasks;
4 |
5 | namespace BilibiliUtilities.Utils.CentralUtils
6 | {
7 | public class VideoUtil
8 | {
9 |
10 | private static readonly string _tb = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF";
11 | private static readonly Dictionary _tr = new Dictionary();
12 | private static readonly List _s = new List { 11, 10, 3, 8, 4, 6 };
13 | private static readonly List _r = new List{ 'B', 'V', '1', 'n', 'n', '4', 'n', '1', 'n', '7', 'n', 'n' };
14 | private static readonly ulong _xor = 177451812;
15 | private const ulong Add = 8728348608;
16 |
17 |
18 | public static async Task BvToAv(string bv)
19 | {
20 |
21 | for (ulong i = 0; i < 58; ++i)
22 | {
23 | _tr[_tb[(int)i]] = i;
24 | }
25 | ulong r = 0;
26 | for (int i = 0; i < 6; ++i)
27 | {
28 | r += _tr[bv[_s[i]]] * (ulong)Math.Pow(58, i);
29 | }
30 | return (r - Add) ^ _xor;
31 | }
32 |
33 | public static async Task AvToBv(ulong av)
34 | {
35 | for (ulong i = 0; i < 58; ++i)
36 | {
37 | _tr[_tb[(int)i]] = i;
38 | }
39 | av = (av ^ _xor) + Add;
40 | for (int i = 0; i < 6; ++i)
41 | {
42 | _r[_s[i]] = _tb[(int)(av / (ulong)Math.Pow(58, i) % 58)];
43 | }
44 | return string.Join("", _r);
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Utils/LiveUtils/RoomUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Net;
7 | using System.Net.Http;
8 | using System.Net.Sockets;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using Newtonsoft.Json;
12 | using Newtonsoft.Json.Linq;
13 |
14 | namespace BilibiliUtilities.Utils.LiveUtils
15 | {
16 | public class RoomUtil
17 | {
18 | private static readonly HttpClient _httpClient = new HttpClient {Timeout = TimeSpan.FromSeconds(20)};
19 |
20 | public static async Task GetLongRoomId(long shortRoomId)
21 | {
22 | var tmpData = JObject.Parse(await _httpClient.GetStringAsync($"https://api.live.bilibili.com/room/v1/Room/room_init?id={shortRoomId}"));
23 | if (int.Parse(tmpData["code"].ToString())!=0)
24 | {
25 | return 0;
26 | }
27 | return int.Parse(tmpData["data"]["room_id"].ToString());
28 | }
29 |
30 | public static async Task GetRoomTokenByShortRoomId(long shortRoomId)
31 | {
32 | var tmpData = JObject.Parse(await _httpClient.GetStringAsync($"https://api.live.bilibili.com/room/v1/Room/room_init?id={shortRoomId}"));
33 | if (int.Parse(tmpData["code"].ToString()) != 0)
34 | {
35 | return "";
36 | }
37 | var roomId = int.Parse(tmpData["data"]["room_id"].ToString());
38 | tmpData = JObject.Parse(await _httpClient.GetStringAsync(
39 | $"https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id={roomId}&platform=pc&player=web"));
40 | //连接的令牌
41 | var token = tmpData["data"]["token"].ToString();
42 | return token;
43 | }
44 |
45 | public static async Task ConnectRoomByShortRoomId(TcpClient tcpClient,long shortRoomId)
46 | {
47 | var roomId = await GetRoomTokenByShortRoomId(shortRoomId);
48 | if (roomId.Equals(""))
49 | {
50 | return false;
51 | }
52 | var tmpData = JObject.Parse(await _httpClient.GetStringAsync(
53 | $"https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id={roomId}&platform=pc&player=web"));
54 | //解析域名,拿取IP地址,用于连接
55 | var chatHost = tmpData["data"]["host"].ToString();
56 | var ips = await Dns.GetHostAddressesAsync(chatHost);
57 | //连接的端口
58 | var chatPort = int.Parse(tmpData["data"]["port"].ToString());
59 | Random random = new Random();
60 | //随机一个选择域名解析出来的IP,负载均衡
61 | await tcpClient.ConnectAsync(ips[random.Next(ips.Length)], chatPort);
62 | if (!tcpClient.Connected)
63 | {
64 | return false;
65 | }
66 | if (!tcpClient.GetStream().CanWrite)
67 | {
68 | return false;
69 | }
70 | return true;
71 | }
72 | }
73 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.Utils/UserUtils/UserUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.IO;
5 | using System.IO.Compression;
6 | using System.Net;
7 | using System.Net.Http;
8 | using System.Net.Sockets;
9 | using System.Text;
10 | using System.Threading.Tasks;
11 | using Newtonsoft.Json;
12 | using Newtonsoft.Json.Linq;
13 |
14 | namespace BilibiliUtilities.Utils.UserUtils
15 | {
16 | public class UserUtil
17 | {
18 | private static readonly HttpClient _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(20) };
19 |
20 | public static async Task GetUserInfoByUid(long uid)
21 | {
22 | var tmpData = JObject.Parse(await _httpClient.GetStringAsync($"https://api.bilibili.com/x/space/acc/info?mid={uid}"));
23 | if (int.Parse(tmpData["code"].ToString()) != 0)
24 | {
25 | return "";
26 | }
27 |
28 | //用户信息
29 | var data = tmpData["data"];
30 | return data;
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/Src/BilibiliUtilities.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BilibiliUtilities.Live", "BilibiliUtilities.Live\BilibiliUtilities.Live.csproj", "{80E78F59-81B6-41FD-B2E2-3A6A689ADD4A}"
3 | EndProject
4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BilibiliUtilities.Utils", "BilibiliUtilities.Utils\BilibiliUtilities.Utils.csproj", "{5372403D-0DA1-4E8F-A7F5-7E39BEA9FC6F}"
5 | EndProject
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BilibiliUtilities.Test", "BilibiliUtilities.Test\BilibiliUtilities.Test.csproj", "{97571412-F61C-4944-8CEC-CA407BD000C9}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EndianBitConverter", "EndianBitConverter\EndianBitConverter.csproj", "{39070DFB-3AC8-4B56-AF4B-1179EDEB038A}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {80E78F59-81B6-41FD-B2E2-3A6A689ADD4A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {80E78F59-81B6-41FD-B2E2-3A6A689ADD4A}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {80E78F59-81B6-41FD-B2E2-3A6A689ADD4A}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {80E78F59-81B6-41FD-B2E2-3A6A689ADD4A}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {5372403D-0DA1-4E8F-A7F5-7E39BEA9FC6F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {5372403D-0DA1-4E8F-A7F5-7E39BEA9FC6F}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {5372403D-0DA1-4E8F-A7F5-7E39BEA9FC6F}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {5372403D-0DA1-4E8F-A7F5-7E39BEA9FC6F}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {97571412-F61C-4944-8CEC-CA407BD000C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {97571412-F61C-4944-8CEC-CA407BD000C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {97571412-F61C-4944-8CEC-CA407BD000C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {97571412-F61C-4944-8CEC-CA407BD000C9}.Release|Any CPU.Build.0 = Release|Any CPU
28 | {39070DFB-3AC8-4B56-AF4B-1179EDEB038A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
29 | {39070DFB-3AC8-4B56-AF4B-1179EDEB038A}.Debug|Any CPU.Build.0 = Debug|Any CPU
30 | {39070DFB-3AC8-4B56-AF4B-1179EDEB038A}.Release|Any CPU.ActiveCfg = Release|Any CPU
31 | {39070DFB-3AC8-4B56-AF4B-1179EDEB038A}.Release|Any CPU.Build.0 = Release|Any CPU
32 | EndGlobalSection
33 | EndGlobal
34 |
--------------------------------------------------------------------------------
/Src/EndianBitConverter/BigEndianBitConverter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (C) Microsoft Corporation. All rights reserved.
2 |
3 | namespace EndianBitConverter
4 | {
5 | ///
6 | /// A big-endian BitConverter that converts base data types to an array of bytes, and an array of bytes to base data types. All conversions are in
7 | /// big-endian format regardless of machine architecture.
8 | ///
9 | internal class BigEndianBitConverter : EndianBitConverter
10 | {
11 | // Instance available from EndianBitConverter.BigEndian
12 | internal BigEndianBitConverter() { }
13 |
14 | public override bool IsLittleEndian { get; } = false;
15 |
16 | public override byte[] GetBytes(short value)
17 | {
18 | return new byte[] { (byte)(value >> 8), (byte)value };
19 | }
20 |
21 | public override byte[] GetBytes(int value)
22 | {
23 | return new byte[] { (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value };
24 | }
25 |
26 | public override byte[] GetBytes(long value)
27 | {
28 | return new byte[] {
29 | (byte)(value >> 56), (byte)(value >> 48), (byte)(value >> 40), (byte)(value >> 32),
30 | (byte)(value >> 24), (byte)(value >> 16), (byte)(value >> 8), (byte)value
31 | };
32 | }
33 |
34 | public override short ToInt16(byte[] value, int startIndex)
35 | {
36 | this.CheckArguments(value, startIndex, sizeof(short));
37 |
38 | return (short)((value[startIndex] << 8) | (value[startIndex + 1]));
39 | }
40 |
41 | public override int ToInt32(byte[] value, int startIndex)
42 | {
43 | this.CheckArguments(value, startIndex, sizeof(int));
44 |
45 | return (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | (value[startIndex + 3]);
46 | }
47 |
48 | public override long ToInt64(byte[] value, int startIndex)
49 | {
50 | this.CheckArguments(value, startIndex, sizeof(long));
51 |
52 | int highBytes = (value[startIndex] << 24) | (value[startIndex + 1] << 16) | (value[startIndex + 2] << 8) | (value[startIndex + 3]);
53 | int lowBytes = (value[startIndex + 4] << 24) | (value[startIndex + 5] << 16) | (value[startIndex + 6] << 8) | (value[startIndex + 7]);
54 | return ((uint)lowBytes | ((long)highBytes << 32));
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Src/EndianBitConverter/EndianBitConverter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (C) Microsoft Corporation. All rights reserved.
2 |
3 | using System;
4 | using System.Runtime.CompilerServices;
5 |
6 | namespace EndianBitConverter
7 | {
8 | ///
9 | /// A BitConverter with a specific endianness that converts base data types to an array of bytes, and an array of bytes to base data types, regardless of
10 | /// machine architecture. Access the little-endian and big-endian converters with their respective properties.
11 | ///
12 | ///
13 | /// The EndianBitConverter implementations provide the same interface as , but exclude those methods which perform the
14 | /// same on both big-endian and little-endian machines (such as ). However, is
15 | /// included for consistency.
16 | ///
17 | public abstract class EndianBitConverter
18 | {
19 | ///
20 | /// Get an instance of a , a BitConverter which performs all conversions in little-endian format regardless of
21 | /// machine architecture.
22 | ///
23 | public static EndianBitConverter LittleEndian { get; } = new LittleEndianBitConverter();
24 |
25 | ///
26 | /// Get an instance of a , a BitConverter which performs all conversions in big-endian format regardless of
27 | /// machine architecture.
28 | ///
29 | public static EndianBitConverter BigEndian { get; } = new BigEndianBitConverter();
30 |
31 | ///
32 | /// Indicates the byte order ("endianness") in which data should be converted.
33 | ///
34 | public abstract bool IsLittleEndian { get; }
35 |
36 | ///
37 | /// Returns the specified Boolean value as a byte array.
38 | ///
39 | /// A Boolean value.
40 | /// A byte array with length 1.
41 | /// You can convert a byte array back to a value by calling the method.
42 | public byte[] GetBytes(bool value)
43 | {
44 | return new byte[] { value ? (byte)1 : (byte)0 };
45 | }
46 |
47 | ///
48 | /// Returns the specified Unicode character value as an array of bytes.
49 | ///
50 | /// A character to convert.
51 | /// An array of bytes with length 2.
52 | /// You can convert a byte array back to a value by calling the method.
53 | public byte[] GetBytes(char value)
54 | {
55 | return this.GetBytes((short)value);
56 | }
57 |
58 | ///
59 | /// Returns the specified double-precision floating point value as an array of bytes.
60 | ///
61 | /// The number to convert.
62 | /// An array of bytes with length 8.
63 | /// You can convert a byte array back to a value by calling the method.
64 | public byte[] GetBytes(double value)
65 | {
66 | long val = BitConverter.DoubleToInt64Bits(value);
67 | return this.GetBytes(val);
68 | }
69 |
70 | ///
71 | /// Returns the specified 16-bit signed integer value as an array of bytes.
72 | ///
73 | /// The number to convert.
74 | /// An array of bytes with length 2.
75 | /// You can convert a byte array back to a value by calling the method.
76 | public abstract byte[] GetBytes(short value);
77 |
78 | ///
79 | /// Returns the specified 32-bit signed integer value as an array of bytes.
80 | ///
81 | /// The number to convert.
82 | /// An array of bytes with length 4.
83 | /// You can convert a byte array back to a value by calling the method.
84 | public abstract byte[] GetBytes(int value);
85 |
86 | ///
87 | /// Returns the specified 64-bit signed integer value as an array of bytes.
88 | ///
89 | /// The number to convert.
90 | /// An array of bytes with length 8.
91 | /// You can convert a byte array back to a value by calling the method.
92 | public abstract byte[] GetBytes(long value);
93 |
94 | ///
95 | /// Returns the specified single-precision floating point value as an array of bytes.
96 | ///
97 | /// The number to convert.
98 | /// An array of bytes with length 4.
99 | /// You can convert a byte array back to a value by calling the method.
100 | public byte[] GetBytes(float value)
101 | {
102 | int val = new SingleConverter(value).GetIntValue();
103 | return this.GetBytes(val);
104 | }
105 |
106 | ///
107 | /// Returns the specified 16-bit unsigned integer value as an array of bytes.
108 | ///
109 | /// The number to convert.
110 | /// An array of bytes with length 2.
111 | /// You can convert a byte array back to a value by calling the method.
112 | [CLSCompliant(false)]
113 | public byte[] GetBytes(ushort value)
114 | {
115 | return this.GetBytes((short)value);
116 | }
117 |
118 | ///
119 | /// Returns the specified 32-bit unsigned integer value as an array of bytes.
120 | ///
121 | /// The number to convert.
122 | /// An array of bytes with length 4.
123 | /// You can convert a byte array back to a value by calling the method.
124 | [CLSCompliant(false)]
125 | public byte[] GetBytes(uint value)
126 | {
127 | return this.GetBytes((int)value);
128 | }
129 |
130 | ///
131 | /// Returns the specified 64-bit unsigned integer value as an array of bytes.
132 | ///
133 | /// The number to convert.
134 | /// An array of bytes with length 8.
135 | /// You can convert a byte array back to a value by calling the method.
136 | [CLSCompliant(false)]
137 | public byte[] GetBytes(ulong value)
138 | {
139 | return this.GetBytes((long)value);
140 | }
141 |
142 | ///
143 | /// Returns a Boolean value converted from the byte at a specified position in a byte array.
144 | ///
145 | /// A byte array.
146 | /// The index of the byte within .
147 | ///
148 | /// true if the byte at in is nonzero; otherwise, false.
149 | ///
150 | /// is null.
151 | ///
152 | /// is less than zero or greater than the length of minus 1.
153 | ///
154 | public bool ToBoolean(byte[] value, int startIndex)
155 | {
156 | this.CheckArguments(value, startIndex, 1);
157 |
158 | return value[startIndex] != 0;
159 | }
160 |
161 | ///
162 | /// Returns a Unicode character converted from two bytes at a specified position in a byte array.
163 | ///
164 | /// An array.
165 | /// The starting position within .
166 | /// A character formed by two bytes beginning at .
167 | ///
168 | /// The ToChar method converts the bytes from index to + 1 to a value.
169 | ///
170 | /// is null.
171 | ///
172 | /// is less than zero or greater than the length of minus 2.
173 | ///
174 | public char ToChar(byte[] value, int startIndex)
175 | {
176 | return (char)this.ToInt16(value, startIndex);
177 | }
178 |
179 | ///
180 | /// Returns a double-precision floating point number converted from eight bytes at a specified position in a byte array.
181 | ///
182 | /// An array of bytes.
183 | /// The starting position within .
184 | /// A double precision floating point number formed by eight bytes beginning at .
185 | ///
186 | /// The ToDouble method converts the bytes from index to + 7 to a
187 | /// value.
188 | ///
189 | /// is null.
190 | ///
191 | /// is less than zero or greater than the length of minus 8.
192 | ///
193 | public double ToDouble(byte[] value, int startIndex)
194 | {
195 | long val = this.ToInt64(value, startIndex);
196 | return BitConverter.Int64BitsToDouble(val);
197 | }
198 |
199 | ///
200 | /// Returns a 16-bit signed integer converted from two bytes at a specified position in a byte array.
201 | ///
202 | /// An array of bytes.
203 | /// The starting position within .
204 | /// A 16-bit signed integer formed by two bytes beginning at .
205 | ///
206 | /// The ToInt16 method converts the bytes from index to + 1 to an
207 | /// value.
208 | ///
209 | /// is null.
210 | ///
211 | /// is less than zero or greater than the length of minus 2.
212 | ///
213 | public abstract short ToInt16(byte[] value, int startIndex);
214 |
215 | ///
216 | /// Returns a 32-bit signed integer converted from four bytes at a specified position in a byte array.
217 | ///
218 | /// An array of bytes.
219 | /// The starting position within .
220 | /// A 32-bit signed integer formed by four bytes beginning at .
221 | ///
222 | /// The ToInt32 method converts the bytes from index to + 3 to an
223 | /// value.
224 | ///
225 | /// is null.
226 | ///
227 | /// is less than zero or greater than the length of minus 4.
228 | ///
229 | public abstract int ToInt32(byte[] value, int startIndex);
230 |
231 | ///
232 | /// Returns a 64-bit signed integer converted from eight bytes at a specified position in a byte array.
233 | ///
234 | /// An array of bytes.
235 | /// The starting position within .
236 | /// A 64-bit signed integer formed by eight bytes beginning at .
237 | ///
238 | /// The ToInt64 method converts the bytes from index to + 7 to an
239 | /// value.
240 | ///
241 | /// is null.
242 | ///
243 | /// is less than zero or greater than the length of minus 8.
244 | ///
245 | public abstract long ToInt64(byte[] value, int startIndex);
246 |
247 | ///
248 | /// Returns a single-precision floating point number converted from four bytes at a specified position in a byte array.
249 | ///
250 | /// An array of bytes.
251 | /// The starting position within .
252 | /// A single-precision floating point number formed by four bytes beginning at .
253 | ///
254 | /// The ToSingle method converts the bytes from index to + 3 to a
255 | /// value.
256 | ///
257 | /// is null.
258 | ///
259 | /// is less than zero or greater than the length of minus 4.
260 | ///
261 | public float ToSingle(byte[] value, int startIndex)
262 | {
263 | int val = this.ToInt32(value, startIndex);
264 | return new SingleConverter(val).GetFloatValue();
265 | }
266 |
267 | ///
268 | /// Returns a 16-bit unsigned integer converted from two bytes at a specified position in a byte array.
269 | ///
270 | ///
271 | /// The starting position within .
272 | /// A 16-bit unsigned integer formed by two bytes beginning at .
273 | ///
274 | /// The ToUInt16 method converts the bytes from index to + 1 to a
275 | /// value.
276 | ///
277 | /// is null.
278 | ///
279 | /// is less than zero or greater than the length of minus 2.
280 | ///
281 | [CLSCompliant(false)]
282 | public ushort ToUInt16(byte[] value, int startIndex)
283 | {
284 | return (ushort)this.ToInt16(value, startIndex);
285 | }
286 |
287 | ///
288 | /// Returns a 32-bit unsigned integer converted from four bytes at a specified position in a byte array.
289 | ///
290 | /// An array of bytes.
291 | /// The starting position within .
292 | /// A 32-bit unsigned integer formed by four bytes beginning at .
293 | ///
294 | /// The ToUInt32 method converts the bytes from index to + 3 to a
295 | /// value.
296 | ///
297 | /// is null.
298 | ///
299 | /// is less than zero or greater than the length of minus 4.
300 | ///
301 | [CLSCompliant(false)]
302 | public uint ToUInt32(byte[] value, int startIndex)
303 | {
304 | return (uint)this.ToInt32(value, startIndex);
305 | }
306 |
307 | ///
308 | /// Returns a 64-bit unsigned integer converted from eight bytes at a specified position in a byte array.
309 | ///
310 | /// An array of bytes.
311 | /// The starting position within .
312 | /// A 64-bit unsigned integer formed by the eight bytes beginning at .
313 | ///
314 | /// The ToUInt64 method converts the bytes from index to + 7 to a
315 | /// value.
316 | ///
317 | /// is null.
318 | ///
319 | /// is less than zero or greater than the length of minus 8.
320 | ///
321 | [CLSCompliant(false)]
322 | public ulong ToUInt64(byte[] value, int startIndex)
323 | {
324 | return (ulong)this.ToInt64(value, startIndex);
325 | }
326 |
327 | // Testing showed that this method wasn't automatically being inlined, and doing so offers a significant performance improvement.
328 | #if !NET40
329 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
330 | #endif
331 | internal void CheckArguments(byte[] value, int startIndex, int byteLength)
332 | {
333 | if (value == null)
334 | {
335 | throw new ArgumentNullException(nameof(value));
336 | }
337 |
338 | // confirms startIndex is not negative or too far along the byte array
339 | if ((uint)startIndex > value.Length - byteLength)
340 | {
341 | throw new ArgumentOutOfRangeException(nameof(value));
342 | }
343 | }
344 | }
345 | }
346 |
--------------------------------------------------------------------------------
/Src/EndianBitConverter/EndianBitConverter.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {39070DFB-3AC8-4B56-AF4B-1179EDEB038A}
8 | Library
9 | Properties
10 | EndianBitConverter
11 | EndianBitConverter
12 | v4.7.1
13 | 512
14 |
15 |
16 | AnyCPU
17 | true
18 | full
19 | false
20 | bin\Debug\
21 | DEBUG;TRACE
22 | prompt
23 | 4
24 |
25 |
26 | AnyCPU
27 | pdbonly
28 | true
29 | bin\Release\
30 | TRACE
31 | prompt
32 | 4
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/Src/EndianBitConverter/LittleEndianBitConverter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (C) Microsoft Corporation. All rights reserved.
2 |
3 | namespace EndianBitConverter
4 | {
5 | ///
6 | /// A little-endian BitConverter that converts base data types to an array of bytes, and an array of bytes to base data types. All conversions are in
7 | /// little-endian format regardless of machine architecture.
8 | ///
9 | internal class LittleEndianBitConverter : EndianBitConverter
10 | {
11 | // Instance available from EndianBitConverter.LittleEndian
12 | internal LittleEndianBitConverter() { }
13 |
14 | public override bool IsLittleEndian { get; } = true;
15 |
16 | public override byte[] GetBytes(short value)
17 | {
18 | return new byte[] { (byte)value, (byte)(value >> 8) };
19 | }
20 |
21 | public override byte[] GetBytes(int value)
22 | {
23 | return new byte[] { (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24) };
24 | }
25 |
26 | public override byte[] GetBytes(long value)
27 | {
28 | return new byte[] {
29 | (byte)value, (byte)(value >> 8), (byte)(value >> 16), (byte)(value >> 24),
30 | (byte)(value >> 32), (byte)(value >> 40), (byte)(value >> 48), (byte)(value >> 56)
31 | };
32 | }
33 |
34 | public override short ToInt16(byte[] value, int startIndex)
35 | {
36 | this.CheckArguments(value, startIndex, sizeof(short));
37 |
38 | return (short)((value[startIndex]) | (value[startIndex + 1] << 8));
39 | }
40 |
41 | public override int ToInt32(byte[] value, int startIndex)
42 | {
43 | this.CheckArguments(value, startIndex, sizeof(int));
44 |
45 | return (value[startIndex]) | (value[startIndex + 1] << 8) | (value[startIndex + 2] << 16) | (value[startIndex + 3] << 24);
46 | }
47 |
48 | public override long ToInt64(byte[] value, int startIndex)
49 | {
50 | this.CheckArguments(value, startIndex, sizeof(long));
51 |
52 | int lowBytes = (value[startIndex]) | (value[startIndex + 1] << 8) | (value[startIndex + 2] << 16) | (value[startIndex + 3] << 24);
53 | int highBytes = (value[startIndex + 4]) | (value[startIndex + 5] << 8) | (value[startIndex + 6] << 16) | (value[startIndex + 7] << 24);
54 | return ((uint)lowBytes | ((long)highBytes << 32));
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Src/EndianBitConverter/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.InteropServices;
3 |
4 | // General Information about an assembly is controlled through the following
5 | // set of attributes. Change these attribute values to modify the information
6 | // associated with an assembly.
7 | [assembly: AssemblyTitle("EndianBitConverter")]
8 | [assembly: AssemblyDescription("")]
9 | [assembly: AssemblyConfiguration("")]
10 | [assembly: AssemblyCompany("")]
11 | [assembly: AssemblyProduct("EndianBitConverter")]
12 | [assembly: AssemblyCopyright("Copyright © 2020")]
13 | [assembly: AssemblyTrademark("")]
14 | [assembly: AssemblyCulture("")]
15 |
16 | // Setting ComVisible to false makes the types in this assembly not visible
17 | // to COM components. If you need to access a type in this assembly from
18 | // COM, set the ComVisible attribute to true on that type.
19 | [assembly: ComVisible(false)]
20 |
21 | // The following GUID is for the ID of the typelib if this project is exposed to COM
22 | [assembly: Guid("39070DFB-3AC8-4B56-AF4B-1179EDEB038A")]
23 |
24 | // Version information for an assembly consists of the following four values:
25 | //
26 | // Major Version
27 | // Minor Version
28 | // Build Number
29 | // Revision
30 | //
31 | // You can specify all the values or you can default the Build and Revision Numbers
32 | // by using the '*' as shown below:
33 | // [assembly: AssemblyVersion("1.0.*")]
34 | [assembly: AssemblyVersion("1.0.0.0")]
35 | [assembly: AssemblyFileVersion("1.0.0.0")]
--------------------------------------------------------------------------------
/Src/EndianBitConverter/SingleConverter.cs:
--------------------------------------------------------------------------------
1 | // Copyright (C) Microsoft Corporation. All rights reserved.
2 |
3 | using System.Runtime.InteropServices;
4 |
5 | namespace EndianBitConverter
6 | {
7 | // Converts between Single (float) and Int32 (int), as System.BitConverter does not have a method to do this in all .NET versions.
8 | // A union is used instead of an unsafe pointer cast so we don't have to worry about the trusted environment implications.
9 | [StructLayout(LayoutKind.Explicit)]
10 | internal struct SingleConverter
11 | {
12 | // map int value to offset zero
13 | [FieldOffset(0)]
14 | private int intValue;
15 |
16 | // map float value to offset zero - intValue and floatValue now take the same position in memory
17 | [FieldOffset(0)]
18 | private float floatValue;
19 |
20 | internal SingleConverter(int intValue)
21 | {
22 | this.floatValue = 0;
23 | this.intValue = intValue;
24 | }
25 |
26 | internal SingleConverter(float floatValue)
27 | {
28 | this.intValue = 0;
29 | this.floatValue = floatValue;
30 | }
31 |
32 | internal int GetIntValue() => this.intValue;
33 |
34 | internal float GetFloatValue() => this.floatValue;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------