using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace BarrageGrab.Modles { [Serializable] public class RoomInfo { /// /// Web房间号 /// public string WebRoomId { get; set; } /// /// 房间号 /// public string RoomId { get; set; } /// /// 直播间名称 /// public string Title { get; set; } /// /// 是否直播中 /// public bool IsLive { get; set; } /// /// 实时在线人数 /// public long UserCount { get; set; } /// /// 累计在线人数 /// public string TotalUserCount { get; set; } /// /// 点赞数 /// public long LikeCount { get; set; } /// /// 直播间二维码图像链接 /// public string QrcodeUrl { get; set; } /// /// 直播间地址 /// public string LiveUrl { get; set; } /// /// 封面链接 /// public string Cover { get; set; } /// /// 直播间管理员用户id /// public List AdminUserIds { get; set; } = new List(); /// /// 主播信息 /// public RoomAnchor Owner { get; set; } /// /// 直播间的一些配置 /// public RoomAuth AuthInfo { get; set; } /// /// 自身信息,Api方式没有 /// public OdinData Odin { get; set; } /// /// Response请求头设置的 Ttwid,Api方式没有 /// public string Ttwid { get; set; } /// /// 是否是匿名直播间 /// public bool IsAnonymous { get; set; } public void SetData(JObject room) { this.RoomId = room["id_str"]?.ToString() ?? ""; this.AdminUserIds = room["admin_user_ids_str"]?.Values().ToList() ?? new List(); this.IsLive = room["status"]?.Value() == 2; this.IsAnonymous = room["live_room_mode"]?.Value() == 1; this.Title = room["title"]?.ToString() ?? ""; this.UserCount = long.Parse(room["room_view_stats"]?["display_value"]?.Value() ?? "0"); this.TotalUserCount = room["room_view_stats"]?["total_user_str"]?.Value() ?? "0"; this.LikeCount = room["like_count"]?.Value() ?? 0; //this.QrcodeUrl = rootData["qrcode_url"]?.ToString() ?? ""; this.AuthInfo = room["room_auth"]?.ToObject(); this.Cover = room["cover"]?["url_list"]?.Values().FirstOrDefault() ?? "";//下播情况下没有cover字段 this.AuthInfo = room["room_auth"]?.ToObject(); this.Cover = room["cover"]?["url_list"]?.Values().FirstOrDefault() ?? "";//下播情况下没有cover字段 } public class RoomAnchor { /// /// 用户ID /// public string UserId { get; set; } /// /// SecUid /// public string SecUid { get; set; } /// /// 昵称 /// public string Nickname { get; set; } /// /// 头像地址 /// public string HeadUrl { get; set; } /// /// 关注状态 0未关注,1已关注,... /// public int FollowStatus { get; set; } } public class RoomAuth { /// /// 是否允许聊天 /// public bool Chat { get; set; } /// /// 是否允许弹幕 /// public bool Danmaku { get; set; } /// /// 是否允许送礼物 /// public bool Gift { get; set; } /// /// 是否允许发红包 /// public bool LuckMoney { get; set; } /// /// 是否允许点赞 /// public bool Digg { get; set; } /// /// 是否允许房间贡献者 /// public bool RoomContributor { get; set; } /// /// 是否允许使用道具 /// public bool Props { get; set; } /// /// 是否允许查看用户名片 /// public bool UserCard { get; set; } /// /// 是否允许查看 POI /// public bool POI { get; set; } /// /// 更多主播? /// public int MoreAnchor { get; set; } /// /// banner? /// public int Banner { get; set; } /// /// 分享配置? /// public int Share { get; set; } /// /// 用户角标? /// public int UserCorner { get; set; } /// /// 横屏? /// public int Landscape { get; set; } /// /// 横屏聊天? /// public int LandscapeChat { get; set; } /// /// 公屏? /// public int PublicScreen { get; set; } /// /// 礼物主播魔法表情配置? /// public int GiftAnchorMt { get; set; } /// /// 录屏配置? /// public int RecordScreen { get; set; } /// /// 打赏贴纸配置? /// public int DonationSticker { get; set; } /// /// 小时榜配置? /// public int HourRank { get; set; } /// /// 商务名片配置? /// public int CommerceCard { get; set; } /// /// 语音聊天配置? /// public int AudioChat { get; set; } /// /// 弹幕默认开关 /// public int DanmakuDefault { get; set; } /// /// KTV 点歌? /// public int KtvOrderSong { get; set; } /// /// 选集? /// public int SelectionAlbum { get; set; } /// /// 点赞? /// public int Like { get; set; } /// /// 倍速播放? /// public int MultiplierPlayback { get; set; } /// /// 下载视频? /// public int DownloadVideo { get; set; } /// /// 收藏? /// public int Collect { get; set; } /// /// 定时下播? /// public int TimedShutdown { get; set; } /// /// 快进? /// public int Seek { get; set; } /// /// 举报? /// public int Denounce { get; set; } /// /// 踩? /// public int Dislike { get; set; } /// /// 只看 TA? /// public int OnlyTa { get; set; } /// /// 投屏? /// public int CastScreen { get; set; } /// /// 评论墙? /// public int CommentWall { get; set; } /// /// 弹幕样式 /// public object BulletStyle { get; set; } /// /// 显示游戏插件? /// public int ShowGamePlugin { get; set; } /// /// PK礼物? /// public int VSGift { get; set; } /// /// PK话题? /// public int VSTopic { get; set; } /// /// PK排行榜? /// public int VSRank { get; set; } /// /// 管理员评论墙? /// public int AdminCommentWall { get; set; } /// /// 商务组件? /// public int CommerceComponent { get; set; } /// /// 抖音会员? /// public int DouPlus { get; set; } /// /// 游戏积分玩法? /// public int GamePointsPlaying { get; set; } /// /// 海报? /// public int Poster { get; set; } /// /// 精彩时刻? /// public int Highlights { get; set; } /// /// 打字评论状态? /// public int TypingCommentState { get; set; } /// /// 上下滑动引导? /// public int StrokeUpDownGuide { get; set; } /// /// 右上角统计浮层? /// public int UpRightStatsFloatingLayer { get; set; } /// /// 投屏显式? /// public int CastScreenExplicit { get; set; } /// /// 选集? /// public int Selection { get; set; } /// /// 行业服务? /// public int IndustryService { get; set; } /// /// 竖屏排行榜? /// public int VerticalRank { get; set; } /// /// 进场特效? /// public int EnterEffects { get; set; } /// /// 粉丝团? /// public int FansClub { get; set; } /// /// 表情外显? /// public int EmojiOutside { get; set; } /// /// 售票? /// public int CanSellTicket { get; set; } /// /// 抖音会员人气宝石? /// public int DouPlusPopularityGem { get; set; } /// /// 任务中心? /// public int MissionCenter { get; set; } /// ///扩展屏幕? /// public int ExpandScreen { get; set; } /// /// 粉丝群? /// public int FansGroup { get; set; } /// /// 话题? /// public int Topic { get; set; } /// /// 主播任务? /// public int AnchorMission { get; set; } /// /// 跑马灯? /// public int Teleprompter { get; set; } /// /// 长按? /// public int LongTouch { get; set; } /// /// 特殊样式 /// public SpecialStyle SpecialStyle { get; set; } /// /// 固定聊天? /// public int FixedChat { get; set; } /// /// 竞猜? /// public int QuizGamePointsPlaying { get; set; } } public class ChatStyle { /// /// 是否禁用样式 /// public int UnableStyle { get; set; } /// /// 内容 /// public string Content { get; set; } /// /// 关闭类型 /// public int OffType { get; set; } } public class SpecialStyle { /// /// 聊天样式 /// public ChatStyle Chat { get; set; } /// /// 点赞样式 /// public ChatStyle Like { get; set; } } public class OdinData { public string user_id { get; set; } public string user_type { get; set; } public string user_is_auth { get; set; } public string user_is_login { get; set; } public string user_unique_id { get; set; } } /// /// 将 直播间页面元素 转为房间信息对象 /// /// /// /// public static Tuple TryParseRoomPageHtml(string html, out RoomInfo result) { result = null; if (html.IsNullOrWhiteSpace()) { return Tuple.Create(-1, "无效的页面数据"); } //var roomDataReg = new Regex(@"(?<=self\.__pace_f\.push\(\[1,""a:)\[.+?\](?=\\n?""\]\)[\s\n\r]*?<\/script>)"); //var roomDataReg = new Regex(@"(?<=self\.__pace_f\.push\(\[1,\s*""0:)\[.+?\](?=\\n""?\]\)[\s\n\r]*?<\/script>)"); //var roomDataReg = new Regex(@"(?<=self\.__pace_f\.push\(\[1,\s*""9:)\[.+?\](?=\\n""?\]\)[\s\n\r]*?<\/script>)"); var roomDataReg = new Regex(@"(?<=self\.__pace_f\.push\(\[\d,\s*""\w:.+)\{\\""state.+?\}(?=\]\\n""\]\)[\s\n\r]*?<\/script>)"); //字符串转义符号版本 var match = roomDataReg.Match(html); if (!match.Success) { //Logger.LogError("在通过正则匹配直播页房间信息时失败,可能是官方做了升级"); return Tuple.Create(1, "未能匹配到房间信息"); } var matchData = Regex.Unescape(match.Value); JObject jsonObject = null; try { jsonObject = JObject.Parse(matchData); } catch (Exception ex) { return Tuple.Create(2, "匹配到的房间数据格式有错误"); } //var root = jsonObject["children"][3]; //var roomInfo = root["initialState"]["roomStore"]["roomInfo"]; //var odin = root["initialState"]["userStore"]["odin"]; //var room = roomInfo["room"]; //var roomOwner = roomInfo["anchor"]; var roomInfo = jsonObject["state"]["roomStore"]["roomInfo"]; var odin = jsonObject["state"]["userStore"]["odin"]; var room = roomInfo["room"]; var roomOwner = roomInfo["anchor"]; if (room == null) { return Tuple.Create(3, "房间信息为空,获取失败"); } result = new RoomInfo(); result.Odin = odin?.ToObject(); result.WebRoomId = roomInfo["web_rid"]?.ToString() ?? ""; result.RoomId = roomInfo["roomId"]?.ToString() ?? ""; result.AdminUserIds = room["admin_user_ids_str"]?.Values()?.ToList() ?? new List(); result.IsLive = room["status"]?.Value() == 2; result.Title = room["title"]?.ToString() ?? ""; result.UserCount = room["room_view_stats"]?["display_value"]?.Value() ?? 0; result.TotalUserCount = room["stats"]?["total_user_str"]?.Value() ?? "0"; result.LikeCount = room["like_count"]?.Value() ?? 0; result.QrcodeUrl = roomInfo["qrcode_url"]?.ToString() ?? ""; result.AuthInfo = room["room_auth"]?.ToObject(); result.Cover = room["cover"]?["url_list"]?.Values().FirstOrDefault() ?? "";//下播情况下没有cover字段 if (roomOwner != null) { result.Owner = new RoomInfo.RoomAnchor() { UserId = roomOwner["id_str"].ToString(), Nickname = roomOwner["nickname"].ToString(), SecUid = roomOwner["sec_uid"].ToString(), HeadUrl = roomOwner["avatar_thumb"]["url_list"].Values()?.FirstOrDefault() ?? "", FollowStatus = roomOwner["follow_info"]["follow_status"].Value() }; } return Tuple.Create(0, "succ"); } /// /// 从直播伴侣的直播创建回调中解析房间信息 /// /// /// /// 缓存 /// public static Tuple TryParseStreamPusherCreate(string json, out RoomInfo info) { info = null; JObject res; try { res = JsonConvert.DeserializeObject(json); } catch (Exception) { return Tuple.Create(4, "不是合法的json格式"); } if (res == null) { return Tuple.Create(1, "正文为空"); } var rootData = res["data"]; var ownerInfo = rootData["owner"]; var dto = new RoomInfo(); dto.RoomId = rootData["id_str"]?.ToString() ?? ""; dto.AdminUserIds = rootData["admin_user_ids_str"]?.Values().ToList() ?? new List(); dto.IsLive = rootData["status"]?.Value() == 2; dto.Title = rootData["title"]?.ToString() ?? ""; dto.UserCount = long.Parse(rootData["room_view_stats"]?["display_value"]?.Value() ?? "0"); dto.TotalUserCount = rootData["room_view_stats"]?["total_user_str"]?.Value() ?? "0"; dto.LikeCount = rootData["like_count"]?.Value() ?? 0; dto.QrcodeUrl = $"https://live.douyin.com/{ownerInfo?["display_id"] ?? ""}"; dto.AuthInfo = rootData["room_auth"]?.ToObject(); dto.Cover = rootData["cover"]?["url_list"]?.Values().FirstOrDefault() ?? "";//下播情况下没有cover字段 dto.WebRoomId = rootData["web_rid"]?.ToString() ?? ownerInfo?["display_id"]?.ToString() ?? ""; if (ownerInfo != null) { dto.Owner = new RoomAnchor() { UserId = ownerInfo["id_str"].ToString(), Nickname = ownerInfo["nickname"].ToString(), SecUid = ownerInfo["sec_uid"].ToString(), HeadUrl = ownerInfo["avatar_thumb"]?["url_list"]?.Values()?.FirstOrDefault()?.ToString(), FollowStatus = ownerInfo["follow_info"]?["follow_status"]?.Value() ?? 0 }; } info = dto; return Tuple.Create(0, "succ"); } /// /// 将 EnterRoom Response 转为房间信息对象 /// /// /// /// public static Tuple TryParseRoomEnterResponse(string response, out RoomInfo info) { info = null; JObject res; try { res = JsonConvert.DeserializeObject(response); } catch (Exception) { return Tuple.Create(4, "不是合法的json格式"); } if (res == null) { return Tuple.Create(1, "响应内容为空"); } if (res["status_code"]?.Value() != 0) { return Tuple.Create(2, (res["status_msg"]?.ToString() ?? "响应状态失败")); } var roomInfos = res["data"]?["data"]?.Values().ToArray(); if (roomInfos == null || roomInfos.Length == 0) return Tuple.Create(3, "房间信息列表为空"); var rootData = res["data"]; var room = roomInfos[0]?.ToObject(); var roomOwner = rootData?["user"] ?? room?["owner"]; var dto = new RoomInfo(); dto.SetData(room); dto.QrcodeUrl = rootData["qrcode_url"]?.ToString() ?? ""; if (roomOwner != null) { dto.Owner = new RoomInfo.RoomAnchor() { UserId = roomOwner["id_str"].ToString(), Nickname = roomOwner["nickname"].ToString(), SecUid = roomOwner["sec_uid"].ToString(), HeadUrl = roomOwner["avatar_thumb"]["url_list"].Values()?.FirstOrDefault() ?? "", FollowStatus = roomOwner["follow_info"]["follow_status"].Value() }; } info = dto; return Tuple.Create(0, "succ"); } } }