/// <summary> /// コンストラクタ /// </summary> private PublishStatus(long liveId, XmlNode node) : base(node, "getpublishstatus", LiveUtil.LiveIdString(liveId)) { this.Stream = new StreamType(); this.User = new UserType(); this.RTMP = new RTMPType(); foreach (var childObj in RootNode.ChildNodes) { var child = (XmlNode)childObj; switch (child.Name) { case "stream": this.Stream = new StreamType(child); break; case "user": this.User = new UserType(child); break; case "rtmp": this.RTMP = new RTMPType(child); break; } } }
/// <summary> /// コメントサーバーに接続します。 /// </summary> /// <remarks> /// 接続失敗時には例外が返されます。 /// </remarks> public void Connect(PlayerStatus playerStatus, CookieContainer cc, bool currentRoomOnly, TimeSpan timeout) { lock (ConnectLock) { if (playerStatus == null) { throw new ArgumentNullException("playerStatus"); } // 例外が発生する可能性があります。 var streamInfo = LiveUtil.GetLiveStreamInfo(playerStatus, cc); // 各コメントルームの情報を取得します。 var roomInfoList = GetAllRoomInfo( streamInfo.PlayerStatus, streamInfo.LiveInfo.CommunityLevel); var currentRoomIndex = FindRoomIndex( playerStatus.MS.Port, roomInfoList); var roomList = new List <CommentRoom>(); // 各コメントルームに接続します。 for (var i = 0; i < roomInfoList.Count(); ++i) { if (currentRoomOnly && i != currentRoomIndex) { roomList.Add(null); continue; } var room = new CommentRoom(this, roomInfoList[i], i); // 接続に失敗した場合、例外が返ります。 room.Connect(cc, timeout); roomList.Add(room); } Disconnect(); lock (SyncRoot) { this.connectedRoomCount = 0; this.playerStatus = streamInfo.PlayerStatus; this.publishStatus = streamInfo.PublishStatus; this.liveInfo = streamInfo.LiveInfo; this.roomList = roomList; this.currentRoomIndex = currentRoomIndex; this.cookieContainer = cc; } // フィールド値を設定した後に、OnConnectedRoomを呼びます。 foreach (var room in ClonedCommentRoomList) { OnConnectedRoom(room); } // 接続時のイベントを発生させます。 FireConnected(); } }
/// <summary> /// アラートメッセージを処理します。 /// </summary> private void HandleMessage(string message) { var doc = new XmlDocument(); doc.LoadXml(message); var node = doc.DocumentElement; if (node.Name != "chat") { return; } Log.Trace("Alert {0}", node.InnerText); // 内部テキストは // [放送ID],[チャンネル/コミュニティID/official],[ユーザーID] // となっています。 var values = node.InnerText.Split(','); if (values.Length < 3) { return; } // 放送IDを取得します。 var liveId = StrUtil.ToInt(values[0], 0); if (liveId <= 0) { return; } // 提供者を取得します。 var providerData = LiveUtil.ParseProvider(values[1]); if (providerData == null) { return; } // ユーザー情報を取得します。 var userId = StrUtil.ToInt(values[2], -1); // IDが重複して送られてくることがあるので、 // すでに受信した放送IDならば無視します。 if (!IsContainsLiveId(liveId)) { var e = new LiveAlertedEventArgs( liveId, providerData, userId); OnLiveAlerted(e); AddLiveIdToHistoryBuffer(liveId); } }
/// <summary> /// ニコニコ生放送の情報を取得します。 /// </summary> public static PlayerStatus Create(string liveStr, CookieContainer cc) { var liveId = LiveUtil.GetLiveId(liveStr); if (liveId < 0) { throw new NicoLiveException(LiveStatusCode.InvalidLiveId); } return(Create(liveId, cc)); }
/// <summary> /// アラート情報を作成します。 /// </summary> public static AlertInfo Create() { var node = LiveUtil.GetXml( "http://live.nicovideo.jp/api/getalertinfo", null); if (node == null) { throw new NicoLiveException(LiveStatusCode.NetworkError); } return(new AlertInfo(node)); }
/// <summary> /// コミュニティ番号が含まれる文字列から情報を作成します。 /// </summary> public static CommunityInfo Create(string communityStr, CookieContainer cc) { var id = LiveUtil.GetCommunityId(communityStr); if (id < 0) { throw new NicoLiveException( "コミュニティIDが取得できませんでした。"); } return(Create(id, cc)); }
/// <summary> /// ニコニコ生放送の情報を取得します。 /// </summary> public static PlayerStatus Create(long liveId, CookieContainer cc) { // 生放送IDから放送情報を取得します。 var node = LiveUtil.GetXml( NicoString.GetPlayerStatusUrl(liveId), cc); if (node == null) { throw new NicoLiveException( LiveStatusCode.NetworkError, LiveUtil.LiveIdString(liveId)); } return(CreateFromXml(liveId, node)); }
/// <summary> /// サーバーのメッセージ処理を開始します。 /// </summary> /// <remarks> /// これ以後、メッセージが受信されるようになります。 /// </remarks> public void StartReceiveMessage(int resFrom, int timeout, DateTime?when = null) { using (var result = this.startingMessageLock.Lock()) { if (result == null) { throw new NicoLiveException( "メッセージの受信開始処理をすでに行っています。"); } using (new DebugLock(SyncRoot)) { // 送信するメッセージの組み立て var message = string.Empty; if (when == null) { // 通常のコメント取得 message = NicoString.MakeThreadStart( ThreadId, resFrom); } else { // 過去ログの取得 var waybackkey = LiveUtil.GetWaybackKey( ThreadId, this.commentClient.CookieContainer); message = NicoString.MakeThreadStart( ThreadId, resFrom, this.commentClient.UserId, waybackkey, when.Value); } // メッセージの受信を開始します。 this.startMessageEvent.Reset(); SendMessageSync(message); } // startMessageEventがシグナル状態にならなかった場合、 // サーバーからのメッセージ受信に失敗しています。 if (!this.startMessageEvent.WaitOne(timeout)) { throw new NicoLiveException( "メッセージの受信開始処理に失敗しました。", this.commentClient.LiveIdString); } } }
/// <summary> /// PostKeyを取得します。 /// </summary> private string GetPostKey() { try { var newBlockNo = (this.lastRes + 1) / 100; /*// postkeyがあって、blocknoも変わっていなければ、 * // postkeyを新たに取得する必要はありません。 * if (!string.IsNullOrEmpty(this.postKey) && * newBlockNo == this.prevBlockNo) * { * return this.postKey; * }*/ // postkeyを短時間で取得し続けるとコメント投稿が不可能になって // しまうため、このような時間制限をつけます。 // また、前回投稿よりきっちり3秒後にblocknoが変わった場合でも // 正しいpostkeyが取得できるよう、 // 猶予時間を(3.0 - 0.1)秒 としています。 var waitTime = MinimumWaitTimeForPost - TimeSpan.FromSeconds(0.1); var now = DateTime.Now; if (now < this.lastGetPostKey + waitTime) { return(this.postKey); } // エラー時にも対応できるよう、取得時間は先に設定します。 this.lastGetPostKey = now; // 新たにpostkeyを取得。 this.postKey = LiveUtil.GetPostKey( this.ThreadId, newBlockNo, this.commentClient.CookieContainer); } catch (Exception ex) { Log.ErrorException(ex, "postkeyの取得に失敗しました。"); this.postKey = null; } return(this.postKey); }
/// <summary> /// Urlからコミュニティ情報を取得します。 /// </summary> /// <exception cref="System.TimeoutException" /> /// <exception cref="System.Net.WebException" /> public static CommunityInfo Create(int id, CookieContainer cc) { // ページを取得します。 var responseData = WebUtil.RequestHttp( NicoString.GetCommunityUrl(id), null, cc); // 失敗;; コミュニティエラー時はレスポンスが空になります。 if (responseData == null) { throw new NicoLiveException( "コミュニティページの取得に失敗しました。", LiveUtil.CommunityIdString(id)); } var text = Encoding.UTF8.GetString(responseData); return(CreateFromHtml(id, text)); }
/// <summary> /// Urlから生放送情報を取得します。 /// </summary> public static LiveInfo Create(long liveId, CookieContainer cc) { // urlを取得します。 var responseData = WebUtil.RequestHttp( NicoString.GetLiveUrl(liveId), null, cc); // 失敗;; エラー時はレスポンスが空になります。 if (responseData == null) { throw new NicoException( "放送ページの取得に失敗しました。", LiveUtil.LiveIdString(liveId)); } var text = Encoding.UTF8.GetString(responseData); return(CreateFromHtml(LiveUtil.LiveIdString(liveId), text)); }
/// <summary> /// コンストラクタ /// </summary> private Heartbeat(long liveId, XmlNode node) : base(node, "heartbeat", LiveUtil.LiveIdString(liveId)) { foreach (var childObj in RootNode.ChildNodes) { var child = (XmlNode)childObj; var value = child.InnerText; switch (child.Name) { case "watchCount": this.WatchCount = StrUtil.ToInt(value, -1); break; case "commentCount": this.CommentCount = StrUtil.ToInt(value, -1); break; case "ticket": this.Ticket = value; break; } } }
/// <summary> /// html形式のコミュニティページからコミュニティ情報を作成します。 /// </summary> public static CommunityInfo CreateFromHtml(int id, string pageStr) { var community = new CommunityInfo(); if (string.IsNullOrEmpty(pageStr)) { throw new NicoLiveException( "与えられたページがありません。", LiveUtil.CommunityIdString(id)); } var m = Regex.Match(pageStr, @"<img src=""http://icon\.nimg\.jp/community/\d+/co([0-9]+)\.jpg\?[0-9]+"""); if (!m.Success) { throw new NicoLiveException( "コミュニティIDを取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.Id = int.Parse(m.Groups[1].Value); m = Regex.Match(pageStr, @"<title>\s*(.*)\s*-?\s*ニコニコ[^<]*?\s*</title>"); if (!m.Success) { throw new NicoLiveException( "コミュニティタイトルを取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.Title = WebUtil.DecodeHtmlText(m.Groups[1].Value); m = Regex.Match(pageStr, @"<div class=""cnt2"" style=""[^""]+?"">\s*" + @"((.|\s)*?)\s*</div></div></div>\s*" + @"<!-- subbox profile -->"); if (!m.Success) { m = Regex.Match(pageStr, @"<div id=""community_description""><!-- ID は暫定 -->\s*" + @"()</div>\s*</div>\s*<div id=""cbox_news"">"); if (!m.Success) { throw new NicoLiveException( "コミュニティプロフィールを取得できませんでした。", LiveUtil.CommunityIdString(id)); } } community.Description = WebUtil.DecodeHtmlText(m.Groups[1].Value); // <td nowrap>レベル:</td> // <td> // <strong>13</strong> m = Regex.Match(pageStr, @"<td nowrap>レベル:</td>\s*<td>\s*<strong>([0-9]+)</strong>"); if (!m.Success) { throw new NicoLiveException( "コミュニティのレベルを取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.Level = int.Parse(m.Groups[1].Value); // <td nowrap>メンバー:</td> // <td> // <strong>87</strong> m = Regex.Match(pageStr, @"<td nowrap>メンバー:</td>\s*<td>\s*<strong>([0-9]+)</strong>"); if (!m.Success) { throw new NicoLiveException( "コミュニティの参加メンバー数を取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.NumberOfMembers = int.Parse(m.Groups[1].Value); m = Regex.Match(pageStr, @"オーナー:<a href=""http://www\.nicovideo\.jp/user/([0-9]+)"" " + @"style=""color:#FFF;"" target=""_blank"">\s*" + @"<strong>([^<]+)</strong></a>"); if (!m.Success) { throw new NicoLiveException( "コミュニティのオーナーを取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.OwnerId = int.Parse(m.Groups[1].Value); community.OwnerName = WebUtil.DecodeHtmlText(m.Groups[2].Value); m = Regex.Match(pageStr, @"開設日:<strong>(\d+)年(\d+)月(\d+)日</strong>"); if (!m.Success) { throw new NicoLiveException( "コミュニティの開設日が取得できませんでした。", LiveUtil.CommunityIdString(id)); } community.OpenDate = new DateTime( int.Parse(m.Groups[1].Value), int.Parse(m.Groups[2].Value), int.Parse(m.Groups[3].Value)); return(community); }
/// <summary> /// html形式の生放送ページから生放送情報を作成します。 /// </summary> public static LiveInfo CreateFromHtml(string idString, string pageStr) { var live = new LiveInfo(); if (string.IsNullOrEmpty(pageStr)) { throw new NicoLiveException( "ページが空です。", idString); } var m = ProviderInfoRegex.Match(pageStr); if (!m.Success) { throw new NicoLiveException( "放送の提供元情報を取得できませんでした。", idString); } var provider = LiveUtil.ParseProvider(m.Groups[1].Value); m = VideoInfoRegex.Match(pageStr); if (!m.Success) { throw new NicoLiveException( "生放送情報を取得できませんでした。", idString); } var infoStr = m.Groups[1].Value; m = IdRegex.Match(infoStr); if (!m.Success) { throw new NicoException( "生放送IDを取得できませんでした。", idString); } live.Id = int.Parse(m.Groups[1].Value); m = TitleRegex.Match(infoStr); if (!m.Success) { throw new NicoException( "生放送タイトルを取得できませんでした。", idString); } live.Title = m.Groups[1].Value .Replace("\\'", "'"); m = DescriptionRegex.Match(infoStr); if (!m.Success) { throw new NicoLiveException( "生放送概要を取得できませんでした。", idString); } live.Description = m.Groups[1].Value .Replace("\\'", "'") .Replace("\\r\\n", "\n"); switch (provider.ProviderType) { case ProviderType.Community: SetCommunityInfo(live, pageStr); break; case ProviderType.Channel: SetChannelInfo(live, pageStr); break; case ProviderType.Official: SetOfficialInfo(live, pageStr); break; default: throw new NotImplementedException( "実装されていない放送提供元です。"); } return(live); }