private void _disconnect(Exception e) { if (!_connected) { return; } InfoLogger.SendInfo(_roomid, "INFO", "连接断开"); _connected = false; _client.Close(); _netStream = null; OnDisconnected?.Invoke(this, new DisconnectEvtArgs { Error = e }); }
public async void Start(string savepath) { try { if (IsRunning) { InfoLogger.SendInfo(_roomid, "ERROR", "已经是运行状态了。"); return; } //设置运行状态。 IsRunning = true; //读取设置 var config = Config.Instance; _downloadCommentOption = config.IsDownloadComment; _autoRetry = config.IsAutoRetry; int.TryParse(config.Timeout ?? "2000", out _streamTimeout); //获取真实下载地址 try { _flvUrl = await PathFinder.GetTrueUrl(_roomid); } catch { InfoLogger.SendInfo(_roomid, "ERROR", "未取得下载地址"); Stop(); return; //停止并退出 } flvDownloader = new FlvDownloader(_roomid, savepath, _downloadCommentOption, _commentProvider); flvDownloader.Info += _flvDownloader_Info; CheckStreaming(); try { flvDownloader.Start(_flvUrl); } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "下载视频流时出错:" + e.Message); Stop(); } }catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "未知错误:" + e.Message); Stop(); } }
static public Task <string> GetTrueUrl(string roomid) { return(Task.Run(() => { if (roomid == null) { InfoLogger.SendInfo(roomid, "ERROR", "房间号获取错误。"); throw new Exception("No roomid"); } var apiUrl = "https://api.live.bilibili.com/room/v1/Room/playUrl?cid=" + roomid + "&otype=json&qn=10000&platform=web"; SendStat(roomid); //访问API获取结果 var wc = new WebClient(); wc.Headers.Add("Accept: */*"); wc.Headers.Add("User-Agent: " + Ver.UA); wc.Headers.Add("Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4"); string resultString; try { resultString = wc.DownloadString(apiUrl); } catch (WebException e0) { InfoLogger.SendInfo(roomid, "ERROR", "发送解析请求失败<net>:" + e0.Message); throw; } catch (Exception e) { InfoLogger.SendInfo(roomid, "ERROR", "发送解析请求失败:" + e.Message); throw; } //解析结果 try { var jsonResult = JObject.Parse(resultString); var trueUrl = jsonResult["data"]["durl"][0]["url"].ToString(); InfoLogger.SendInfo(roomid, "INFO", "地址解析成功:" + trueUrl); return trueUrl; } catch (Exception e) { InfoLogger.SendInfo(roomid, "ERROR", "视频流地址解析失败:" + e.Message); throw; } })); }
private async void HeartbeatLoop() { try { while (_connected) { SendHeartbeatAsync(); await Task.Delay(30000); } } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", e.Message); _disconnect(e); } }
private Task <string> GetCmtServer() { return(Task.Run(() => { //获取真实弹幕服务器地址。 InfoLogger.SendInfo(_roomid, "INFO", "开始解析弹幕服务器"); var chatWc = new WebClient(); chatWc.Headers.Add("Accept: */*"); chatWc.Headers.Add("User-Agent: " + Ver.UA); chatWc.Headers.Add("Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4"); var chatApi = "http://live.bilibili.com/api/player?id=cid:" + _roomid; string chatXmlString; try { chatXmlString = chatWc.DownloadString(chatApi); } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "无法解析弹幕服务器:" + e.Message); throw; } //解析弹幕信息Xml chatXmlString = "<root>" + chatXmlString + "</root>"; var chatXml = new XmlDocument(); try { chatXml.LoadXml(chatXmlString); } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "解析XML失败:" + e.Message); throw; } //取得弹幕服务器Url var serverNode = chatXml.DocumentElement?.SelectSingleNode("/root/dm_server"); var cmtServerUrl = serverNode?.InnerText; InfoLogger.SendInfo(_roomid, "INFO", "解析弹幕服务器地址成功:" + cmtServerUrl); return cmtServerUrl; })); }
public void Stop() { if (IsRunning) { IsRunning = false; _recordedSize = 0; if (flvDownloader != null) { flvDownloader.Stop(); flvDownloader = null; } InfoLogger.SendInfo(_roomid, "INFO", "停止"); OnStop?.Invoke(this); } else { InfoLogger.SendInfo(_roomid, "ERROR", "已经是停止状态了"); } }
public async void Connect() { try { var cmtServer = await GetCmtServer(); var cmtHost = cmtServer.Item1; var cmtPort = cmtServer.Item2; if (cmtHost == null) { throw new Exception("无法获得弹幕服务器地址"); } //连接弹幕服务器 _client = new TcpClient(); await _client.ConnectAsync(cmtHost, cmtPort); _netStream = _client.GetStream(); int.TryParse(_roomid, out int roomIdNumber); if (SendJoinChannel(roomIdNumber)) { _connected = true; HeartbeatLoop(); var thread = new Thread(ReceiveMessageLoop) { IsBackground = true }; thread.Start(); } else { InfoLogger.SendInfo(_roomid, "ERROR", "加入频道失败"); throw new Exception("Could't add the channel"); } }catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", e.Message); _disconnect(e); } }
public void Stop(bool isNetError = false) { if (IsRunning) { IsRunning = false; _recordedSize = 0; if (flvDownloader != null) { flvDownloader.Stop(); flvDownloader = null; } InfoLogger.SendInfo(_roomid, "INFO", "停止"); OnStop?.Invoke(this, isNetError); _timer?.Dispose(); } else { InfoLogger.SendInfo(_roomid, "ERROR", "已经是停止状态了"); } }
private async void CheckStreaming() { await Task.Delay(_streamTimeout); try { if (flvDownloader == null) { return; } if (_recordedSize <= 1) { InfoLogger.SendInfo(_roomid, "INFO", "接收流超时。"); Stop(); } }catch (Exception ex) { InfoLogger.SendInfo(_roomid, "ERROR", "在检查直播状态时发生未知错误:" + ex.Message); Stop(); } }
/// <summary> /// 获取弹幕服务器地址和端口。 /// </summary> /// <returns>Tuple<string, int>(弹幕服务器地址, 端口)</returns> private Task <Tuple <string, int> > GetCmtServer() { return(Task.Run(() => { //获取真实弹幕服务器地址。 InfoLogger.SendInfo(_roomid, "INFO", "开始解析弹幕服务器"); var chatWc = new WebClient(); chatWc.Headers.Add("Accept: */*"); chatWc.Headers.Add("User-Agent: " + Ver.UA); chatWc.Headers.Add("Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4"); var chatApi = "https://api.live.bilibili.com/room/v1/Danmu/getConf?room_id=" + _roomid; string chatConfString; try { chatConfString = chatWc.DownloadString(chatApi); } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "无法解析弹幕服务器:" + e.Message); throw; } //解析弹幕信息 try { var chatConf = JObject.Parse(chatConfString); var cmtServerHost = chatConf["data"]["host"].ToString(); int.TryParse(chatConf["data"]["port"].ToString(), out int cmtServerPort); InfoLogger.SendInfo(_roomid, "INFO", $"解析弹幕服务器地址成功:{cmtServerHost}:{cmtServerPort}"); return new Tuple <string, int>(cmtServerHost, cmtServerPort); } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "解析弹幕服务器失败:" + e.Message); throw; } })); }
static public Task <RoomInfo> GetRoomInfo(string originalRoomId) { return(Task.Run(() => { //InfoLogger.SendInfo(originalRoomId, "DEBUG", "正在刷新信息"); var roomWebPageUrl = "https://api.live.bilibili.com/room/v1/Room/get_info?id=" + originalRoomId; var wc = new WebClient(); wc.Headers.Add("Accept: */*"); wc.Headers.Add("User-Agent: " + Ver.UA); wc.Headers.Add("Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4"); //发送HTTP请求 byte[] roomHtml; try { roomHtml = wc.DownloadData(roomWebPageUrl); } catch (Exception e) { InfoLogger.SendInfo(originalRoomId, "ERROR", "获取房间信息失败:" + e.Message); return null; } //解析返回结果 try { var roomJson = Encoding.UTF8.GetString(roomHtml); var result = JObject.Parse(roomJson); var uid = result["data"]["uid"].ToString(); var userInfoUrl = "https://api.bilibili.com/x/web-interface/card?mid=" + uid; var uwc = new WebClient(); uwc.Headers.Add("Accept: */*"); uwc.Headers.Add("User-Agent: " + Ver.UA); uwc.Headers.Add("Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4"); byte[] userHtml; try { userHtml = uwc.DownloadData(userInfoUrl); } catch (Exception e) { InfoLogger.SendInfo(originalRoomId, "ERROR", "获取用户信息失败:" + e.Message); return null; } var userJson = Encoding.UTF8.GetString(userHtml); var userResult = JObject.Parse(userJson); var userName = userResult["data"]["card"]["name"].ToString(); var roominfo = new RoomInfo { realRoomid = result["data"]["room_id"].ToString(), title = result["data"]["title"].ToString(), liveStatus = result["data"]["live_status"].ToString() == "1" ? true : false, username = userName }; return roominfo; } catch (Exception e) { InfoLogger.SendInfo(originalRoomId, "ERROR", "房间信息解析失败:" + e.Message); return null; } })); }
public CommentModel(string json, long time, int version = 1) { Time = time; RawData = json; JsonVersion = version; switch (version) { case 1: { var obj = JArray.Parse(json); CommentText = obj[1].ToString(); CommentUser = obj[2][1].ToString(); MsgType = MsgTypeEnum.Comment; break; } case 2: { var obj = JObject.Parse(json); var cmd = obj["cmd"].ToString(); if (cmd.StartsWith("LIVE")) { MsgType = MsgTypeEnum.LiveStart; RoomId = obj["roomid"].ToString(); } else if (cmd.StartsWith("PREPARING")) { MsgType = MsgTypeEnum.LiveEnd; RoomId = obj["roomid"].ToString(); } else if (cmd.StartsWith("DANMU_MSG")) { CommentText = obj["info"][1].ToString(); CommentUser = obj["info"][2][1].ToString(); IsAdmin = obj["info"][2][2].ToString() == "1"; IsVip = obj["info"][2][3].ToString() == "1"; DmType = Convert.ToInt32(obj["info"][0][1]); Fontsize = Convert.ToInt32(obj["info"][0][2]); Color = Convert.ToInt32(obj["info"][0][3]); SendTimestamp = Convert.ToInt64(obj["info"][0][4]); UserHash = obj["info"][0][7].ToString(); MsgType = MsgTypeEnum.Comment; } else if (cmd.StartsWith("SEND_GIFT")) { MsgType = MsgTypeEnum.GiftSend; GiftName = obj["data"]["giftName"].ToString(); GiftUser = obj["data"]["uname"].ToString(); Giftrcost = obj["data"]["rcost"].ToString(); GiftNum = obj["data"]["num"].ToString(); } else if (cmd.StartsWith("GIFT_TOP")) { MsgType = MsgTypeEnum.GiftTop; } else if (cmd.StartsWith("WELCOME")) { MsgType = MsgTypeEnum.Welcome; CommentUser = obj["data"]["uname"].ToString(); IsVip = true; IsAdmin = obj["data"]["isadmin"].ToString() == "1"; } else if (cmd == "SUPER_CHAT_MESSAGE") { MsgType = MsgTypeEnum.SuperChatMessage; var price = obj["data"]["price"].ToString(); CommentText = $"SC ¥{price} " + obj["data"]["message"].ToString(); CommentUser = obj["data"]["user_info"]["uname"].ToString(); Fontsize = 40; SendTimestamp = Convert.ToInt64(obj["data"]["ts"]); InfoLogger.SendInfo("debug", "superchat", CommentText); } else { MsgType = MsgTypeEnum.Unknown; } break; } default: throw new Exception("无法解析的弹幕数据"); } }
public async void Start(string savepath) { try { if (IsRunning) { InfoLogger.SendInfo(_roomid, "ERROR", "已经是运行状态了。"); return; } //设置运行状态。 IsRunning = true; //读取设置 var config = Config.Instance; _downloadCommentOption = config.IsDownloadComment; _autoRetry = config.IsAutoRetry; int.TryParse(config.Timeout ?? "2000", out _streamTimeout); //获取真实下载地址 try { _flvUrl = await PathFinder.GetTrueUrl(_roomid); } catch (WebException) { InfoLogger.SendInfo(_roomid, "ERROR", "未取得下载地址<net>"); Stop(true); return; //停止并退出 } catch { InfoLogger.SendInfo(_roomid, "ERROR", "未取得下载地址"); Stop(); return; //停止并退出 } flvDownloader = new FlvDownloader(_roomid, savepath, _downloadCommentOption, _commentProvider); flvDownloader.Info += _flvDownloader_Info; CheckStreaming(); _larecordedSize = _recordedSize = 0; try { flvDownloader.Start(_flvUrl); } catch (WebException e) { InfoLogger.SendInfo(_roomid, "ERROR", "下载视频流时出错<net>:" + e.Message); Stop(true); return; //停止并退出 } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "下载视频流时出错:" + e.Message); Stop(); } _timer = new Timer((e) => { if (_larecordedSize == _recordedSize) { InfoLogger.SendInfo(_roomid, "INFO", "接收流超时,超时时间内文件大小未有变动。"); Stop(true); } else { _larecordedSize = _recordedSize; } }, null, _streamTimeout, _streamTimeout); } catch (WebException e) { InfoLogger.SendInfo(_roomid, "ERROR", "未知错误<net>:" + e.Message); Stop(true); return; //停止并退出 } catch (Exception e) { InfoLogger.SendInfo(_roomid, "ERROR", "未知错误:" + e.Message); Stop(); } }