private void WebSocket_Received(object sender, IPacket e) { try { if (e is PacketMessageEventMessageChat chat) { var comment = Tools.Parse(chat.Comment); var commentData = Tools.CreateCommentData(comment, _startAt, _siteOptions); var messageContext = CreateMessageContext(comment, commentData, false); if (messageContext != null) { MessageReceived?.Invoke(this, messageContext); } } else if (e is PacketMessageEventMessageAudienceCount audienceCount) { var ac = audienceCount.AudienceCount; MetadataUpdated?.Invoke(this, new Metadata { CurrentViewers = ac.live_viewers.ToString(), TotalViewers = ac.viewers.ToString(), }); } else if (e is PacketMessageEventMessageLiveEnd liveEnd) { Disconnect(); } } catch (Exception ex) { _logger.LogException(ex); } }
public async Task ReceiveAsync() { _isDisconnectRequested = false; _cts = new CancellationTokenSource(); while (true) { if (_isDisconnectRequested) { break; } var liveInfo = await API.GetStreamAsync(_server, _channelName); if (liveInfo != null) { MetadataUpdated?.Invoke(this, liveInfo); } try { await Task.Delay(_pollingIntervalSec * 1000, _cts.Token); } catch (TaskCanceledException) { break; } } }
public async Task ReceiveAsync(long live_id, long lastUpdatedAt, CookieContainer cc) { long lua = lastUpdatedAt; _isDisconnectRequested = false; _cts = new CancellationTokenSource(); while (true) { if (_isDisconnectRequested) { break; } var liveData = await Api.GetLiveDataAsync(_server, live_id, lua, cc); lua = liveData.UpdatedAt; MetadataUpdated?.Invoke(this, liveData); try { await Task.Delay(_pollingIntervalSec * 1000, _cts.Token); } catch (TaskCanceledException) { break; } } }
private void OnMessageReceived(string data) { var message = MessageParser.ParseMessage(data, SendInfo); if (message is UnknownMessage) { _logger.LogException(new ParseException(data)); return; } if (message != null) { if (message is IMirrativDisconnected disconnected) { this.Stop(); } MessageReceived?.Invoke(this, message); } if (message is IMirrativJoinRoom join) { MetadataUpdated?.Invoke(this, new Metadata { CurrentViewers = join.OnlineViewerNum.ToString(), }); } }
private void PromptyStatsProvider_Received(object sender, IPromptyStats e) { MetadataUpdated?.Invoke(this, new Metadata { IsLive = e.LiveStatus == "LIVE", TotalViewers = e.ViewerCount.ToString(), }); }
private void RaiseMetadataUpdated(LiveData liveData) { MetadataUpdated?.Invoke(this, new Metadata { Title = liveData.Live.Title, CurrentViewers = liveData.Live.ViewCount.ToString(), TotalViewers = liveData.Live.TotalViewCount.ToString(), }); }
private void _500msTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { var elapsed = DateTime.Now - _startAt; MetadataUpdated?.Invoke(this, new Metadata { Elapsed = Tools.ElapsedToString(elapsed), }); }
public void HandleData(GlobalUpdate eventData) { GlobalUpdated?.Invoke(this, new NestGlobalEventArgs(eventData)); DeviceUpdated?.Invoke(this, new NestDeviceEventArgs(eventData.Devices)); StructureUpdated?.Invoke(this, new NestStructureEventArgs(eventData.Structures)); ThermostatUpdated?.Invoke(this, new NestThermostatEventArgs(eventData.Thermostats)); CameraUpdated?.Invoke(this, new NestCameraEventArgs(eventData.Cameras)); SmokeCOAlarmUpdated?.Invoke(this, new NestSmokeCOAlarmEventArgs(eventData.SmokeCOAlarms)); MetadataUpdated?.Invoke(this, new NestMetadataEventArgs(eventData.Metadata)); }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { var cc = GetCookieContainer(browserProfile); var list = GetCommentProviderInternals(_options, _siteOptions, _userStoreManager, _dataSource, _logger, this, SiteContextGuid); var cu = await GetCurrentUserInfo(browserProfile); if (cu.IsLoggedIn) { foreach (var f in list) { var isValid = f.IsValidInput(input); if (isValid) { _internal = f; break; } } } else { //未ログインでもWebSocket経由なら取れる。 var f = new NewLiveInternalProvider(_options, _siteOptions, _userStoreManager, _logger, _dataSource) { SiteContextGuid = SiteContextGuid, }; var isValid = f.IsValidInput(input); if (isValid) { _internal = f; } } if (_internal == null) { //非対応のInput //AfterDisconnected(); return; } BeforeConnect(); _internal.MetadataUpdated += (s, e) => MetadataUpdated?.Invoke(s, e); _internal.MessageReceived += (s, e) => MessageReceived?.Invoke(s, e); try { await _internal.ConnectAsync(input, cc); } catch (Exception ex) { throw new NicoException("", $"input={input},browser={browserProfile.Type}({browserProfile.ProfileName})", ex); } finally { AfterDisconnected(); } }
private void ElapsedTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (_startedAt.HasValue) { var elapsed = (GetCurrentDateTime().ToUniversalTime() - _startedAt.Value); MetadataUpdated?.Invoke(this, new Metadata { Elapsed = Tools.ElapsedToString(elapsed), }); } }
private void ElapsedTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { if (!_startedAt.HasValue) { return; } var elapsed = GetCurrentDateTime() - _startedAt.Value; var metadata = new Metadata { Elapsed = SitePluginCommon.Utils.ElapsedToString(elapsed), }; MetadataUpdated?.Invoke(this, metadata); }
private void OnMessageReceived(string data) { var message = ParseMessage(data, SendInfo); if (message != null) { MessageReceived?.Invoke(this, message); } if (message is IMirrativJoinRoom join) { MetadataUpdated?.Invoke(this, new Metadata { CurrentViewers = join.OnlineViewerNum.ToString(), }); } }
private void MetaProvider_MetadataUpdated(object sender, Stream e) { var stream = e; Debug.Assert(stream != null); if (!_startedAt.HasValue) { _startedAt = stream.StartedAt; _elapsedTimer.Enabled = true; } var metadata = new Metadata { Title = stream.Title, CurrentViewers = stream.ViewerCount.ToString(), }; MetadataUpdated?.Invoke(this, metadata); }
private void WebSocket_Received(object sender, Packet p) { try { if (p.IsStatus()) { MetadataUpdated?.Invoke(this, new Metadata { Title = p.Title, Elapsed = Tools.ElapsedToString(p.Elapsed), Others = p.DisplayPointString(), }); LiveStatus = p.Status; } else if ((MixchMessageType)p.Kind == MixchMessageType.PoiPoi) { lock (_poipoiStockDict) { if (_poipoiStockDict.ContainsKey(p.PoiPoiKey())) { var _p = _poipoiStockDict[p.PoiPoiKey()]; _p.Count += p.Count; } else { _poipoiStockDict[p.PoiPoiKey()] = p; } } } else if (p.HasMessage()) { var messageContext = CreateMessageContext(p, false); if (messageContext != null) { MessageReceived?.Invoke(this, messageContext); } } } catch (Exception ex) { _logger.LogException(ex); } _lastReceiveTime = DateTime.Now; }
private async Task ReceiveAsync() { FinishReason = ProviderFinishReason.Unknown; _isDisconnectRequested = false; _cts = new CancellationTokenSource(); while (true) { if (_isDisconnectRequested) { FinishReason = ProviderFinishReason.ByStopMethod; break; } var liveInfo = await GetLiveInfoAsync(); MetadataUpdated?.Invoke(this, liveInfo); await Wait(); } }
protected virtual Task CreateMetadataReceivingTask(ref IMetadataProvider metaProvider, BrowserType browserType, string vid, string liveChatHtml) { Task metaTask = null; YtCfg ytCfg = null; try { var ytCfgStr = Tools.ExtractYtcfg(liveChatHtml); ytCfg = new YtCfg(ytCfgStr); } catch (ParseException ex) { _logger.LogException(ex, "live_chatからのytcfgの抜き出しに失敗", liveChatHtml); } if (ytCfg != null) { //"service_ajax?name=updatedMetadataEndpoint"はIEには対応していないらしく、400が返って来てしまう。 //そこで、IEの場合のみ旧版の"youtubei"を使うようにした。 if (browserType == BrowserType.IE) { metaProvider = new MetaDataYoutubeiProvider(_server, _logger); } else { metaProvider = new MetadataProvider(_server, _logger); } metaProvider.MetadataReceived += (s, e) => { MetadataUpdated?.Invoke(this, e); }; metaProvider.InfoReceived += (s, e) => { SendInfo(e.Comment, e.Type); }; metaTask = metaProvider.ReceiveAsync(ytCfg: ytCfg, vid: vid, cc: _cc); } return(metaTask); }
public async Task ReceiveAsync() { _isDisconnectRequested = false; _cts = new CancellationTokenSource(); while (true) { if (_isDisconnectRequested) { break; } var liveInfo = await Api.PollLiveAsync(_server, _liveId); MetadataUpdated?.Invoke(this, liveInfo); try { await Task.Delay(_siteOptions.PollingIntervalSec * 60 * 1000, _cts.Token); } catch (TaskCanceledException) { break; } } }
private void _rpc_OnMetadataUpdated(RPCController sender, long peerId, MetadataVariable value) { if (_disposing) { return; } if (!Devices.ContainsKey(peerId)) { ReloadRequired?.Invoke(this, ReloadType.Full); return; } Device device = Devices[peerId]; if (!device.Metadata.ContainsKey(value.Name)) { DeviceReloadRequired?.Invoke(this, device, null, DeviceReloadType.Metadata); return; } MetadataVariable variable = device.Metadata[value.Name]; variable.SetValue(value); MetadataUpdated?.Invoke(this, device, variable); }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { BeforeConnect(); var autoReconnectMode = false; var cc = GetCookieContainer(browserProfile); await InitLoveIconUrlDict(); string channelId; string liveId; try { (channelId, liveId) = GetLiveIdFromInput(input); } catch (InvalidUrlException) { SendSystemInfo("入力されたURLが正しくないようです", InfoType.Error); AfterDisconnected(); return; } if (string.IsNullOrEmpty(liveId)) { autoReconnectMode = true; } while (true) { if (autoReconnectMode) { MetadataUpdated?.Invoke(this, new Metadata { Title = "(放送が始まるまで待機しています)", }); try { liveId = await GetLiveIdFromChannelId(_server, channelId); } catch (LiveNotFoundException) { try { SendSystemInfo((LiveCheckIntervalMs / 1000) + "秒待ってから放送しているか確認します", InfoType.Debug); await Task.Delay(LiveCheckIntervalMs, _cts.Token); } catch (TaskCanceledException) { break; } continue; } } var(liveInfo, raw) = await GetLiveInfo(channelId, liveId); if (liveInfo.LiveStatus == "FINISHED") { SendSystemInfo("配信が終了しました", InfoType.Notice); if (autoReconnectMode) { continue; } else { break; } } else { SendSystemInfo("LiveStatus=" + liveInfo.LiveStatus, InfoType.Debug); } MetadataUpdated?.Invoke(this, new Metadata { Title = liveInfo.Title, }); var url = liveInfo.ChatUrl; _provider = CreateMessageProvider(); _provider.Opened += Provider_Opened; _provider.Received += Provider_Received; var messageProviderTask = _provider.ReceiveAsync(url); var tasks = new List <Task>(); tasks.Add(messageProviderTask); var promptyStatsProvider = CreatePromptyStatsProvider(); promptyStatsProvider.Received += PromptyStatsProvider_Received; var promptyStatsTask = promptyStatsProvider.ReceiveAsync(channelId, liveId); tasks.Add(promptyStatsTask); var blacklistProvider = CreateBlackListProvider(); blacklistProvider.Received += BlacklistProvider_Received; var blackListTask = blacklistProvider.ReceiveAsync(Tools.ExtractCookies(cc)); tasks.Add(blackListTask); while (true) { var t = await Task.WhenAny(tasks); if (t == messageProviderTask) { //messageProviderTaskが何かしらの理由で終了したから色々終了させる。 promptyStatsProvider.Disconnect(); blacklistProvider.Disconnect(); await RemoveTaskFromList(messageProviderTask, tasks); await RemoveTaskFromList(promptyStatsTask, tasks); await RemoveTaskFromList(blackListTask, tasks); break; } else if (t == promptyStatsTask) { await RemoveTaskFromList(promptyStatsTask, tasks); } else if (t == blackListTask) { await RemoveTaskFromList(blackListTask, tasks); } } SendSystemInfo("websocketが切断", InfoType.Debug); if (_isUserDisconnected) { break; } else { SendSystemInfo("websocketの切断がユーザによるものではないため放送ステータスを確認", InfoType.Debug); } try { await AfterMessageProviderDisconnected(); } catch (Exception) { break; } } if (autoReconnectMode) { //タイトルが(放送が始まるまで待機しています)となっている可能性を考慮して消す MetadataUpdated?.Invoke(this, new Metadata { Title = "", }); } AfterDisconnected(); }
private void MessageProvider_MetaReceived(object sender, IMetadata e) { MetadataUpdated?.Invoke(this, e); }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { if (string.IsNullOrEmpty(input)) { return; } BeforeConnect(); MetadataUpdated?.Invoke(this, new Metadata { Active = "-", CurrentViewers = "-", Elapsed = "-", Title = "-", TotalViewers = "-", }); string vid = null; bool isInputStoringNeeded = false; var resolver = new VidResolver(); try { var result = await resolver.GetVid(_server, input); if (result is MultiVidsResult multi) { SendInfo("このチャンネルでは複数のライブが配信中です。", InfoType.Notice); foreach (var v in multi.Vids) { SendInfo(v, InfoType.Notice);//titleも欲しい } } else if (result is VidResult vidResult) { vid = vidResult.Vid; } else if (result is NoVidResult no) { SendInfo("このチャンネルでは生放送をしていないようです", InfoType.Error); } else { throw new NotSupportedException(); } } catch (HttpRequestException ex) { SendInfo("Http error", InfoType.Error); _logger.LogException(ex, "Http error", "input=" + input); AfterConnect(); return; } catch (YtInitialDataNotFoundException ex) { SendInfo("ytInitialDataが存在しません", InfoType.Error); _logger.LogException(ex); AfterConnect(); return; } catch (Exception ex) { SendInfo("入力されたURLは存在しないか無効な値です", InfoType.Error); _logger.LogException(ex, "Invalid input", "input=" + input); AfterConnect(); return; } if (string.IsNullOrEmpty(vid)) { AfterConnect(); return; } if (resolver.IsChannel(input) || resolver.IsCustomChannel(input) || resolver.IsUser(input)) { isInputStoringNeeded = true; } var html = await _server.GetAsync($"https://www.youtube.com/watch?v={vid}"); var liveBroadcastDetails = Tools.ExtractLiveBroadcastDetailsFromLivePage(html); if (liveBroadcastDetails != null) { dynamic d = Newtonsoft.Json.JsonConvert.DeserializeObject(liveBroadcastDetails); if (d.ContainsKey("startTimestamp")) { var startedStr = (string)d.startTimestamp; _startedAt = DateTime.Parse(startedStr); _elapsedTimer.Interval = 500; _elapsedTimer.Elapsed += (s, e) => { if (!_startedAt.HasValue) { return; } var elapsed = DateTime.Now - _startedAt.Value; MetadataUpdated?.Invoke(this, new Metadata { Elapsed = Tools.ToElapsedString(elapsed), }); }; _elapsedTimer.Enabled = true; } } _cc = CreateCookieContainer(browserProfile); var userCommentCountDict = CreateUserCommentCountDict(); _connection = CreateConnection(_logger, _cc, _options, _server, _siteOptions, userCommentCountDict, _receivedCommentIds, this, _userStoreManager, SiteContextGuid); _connection.Connected += (s, e) => { Connected?.Invoke(this, new ConnectedEventArgs { IsInputStoringNeeded = isInputStoringNeeded }); }; _connection.MessageReceived += (s, e) => MessageReceived?.Invoke(s, e); _connection.MetadataUpdated += (s, e) => MetadataUpdated?.Invoke(s, e); _connection.LoggedInStateChanged += (s, e) => LoggedInStateChanged?.Invoke(s, e); var reloadManager = new ReloadManager() { CountLimit = 5, CountCheckTimeRangeMin = 1, }; reload: if (_disconnectedByUser) { AfterConnect(); return; } if (!reloadManager.CanReload()) { SendInfo($"{reloadManager.CountCheckTimeRangeMin}分以内に{reloadManager.CountLimit}回再接続を試みました。サーバーに余計な負荷を掛けるのを防ぐため自動再接続を中断します", InfoType.Error); AfterConnect(); return; } reloadManager.SetTime(); try { var disconnectReason = await _connection.ReceiveAsync(vid, browserProfile.Type); switch (disconnectReason) { case DisconnectReason.Reload: SendInfo("エラーが発生したためサーバーとの接続が切断されましたが、自動的に再接続します", InfoType.Error); goto reload; case DisconnectReason.ByUser: SendInfo("ユーザーが切断ボタンを押したため切断しました", InfoType.Debug); break; case DisconnectReason.Finished: SendInfo("配信が終了しました", InfoType.Notice); break; case DisconnectReason.ChatUnavailable: SendInfo("この配信ではチャットが無効になっているようです", InfoType.Error); break; case DisconnectReason.YtInitialDataNotFound: SendInfo("ytInitialDataの取得に失敗しました", InfoType.Error); break; case DisconnectReason.ServerError: SendInfo("サーバでエラーが発生したため接続できませんでした", InfoType.Error); break; case DisconnectReason.Unknown: SendInfo("原因不明のエラーが発生したため切断されましたが、自動的に再接続します", InfoType.Error); goto reload; } } catch (Exception ex) { _logger.LogException(ex, "", $"input={input}"); SendInfo("回復不能なエラーが発生しました", InfoType.Error); } AfterConnect(); }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { if (string.IsNullOrEmpty(input)) { return; } BeforeConnect(); MetadataUpdated?.Invoke(this, new Metadata { Active = "-", CurrentViewers = "-", Elapsed = "-", Title = "-", TotalViewers = "-", }); string vid = null; bool isInputStoringNeeded = false; var resolver = new VidResolver(); try { var result = await resolver.GetVid(_server, input); if (result is MultiVidsResult multi) { SendInfo("このチャンネルでは複数のライブが配信中です。", InfoType.Notice); foreach (var v in multi.Vids) { SendInfo(v, InfoType.Notice);//titleも欲しい } } else if (result is VidResult vidResult) { vid = vidResult.Vid; } else if (result is NoVidResult no) { SendInfo("このチャンネルでは生放送をしていないようです", InfoType.Error); } else { throw new NotSupportedException(); } } catch (HttpRequestException ex) { SendInfo("Http error", InfoType.Error); _logger.LogException(ex, "Http error", "input=" + input); AfterConnect(); return; } catch (YtInitialDataNotFoundException ex) { SendInfo("ytInitialDataが存在しません", InfoType.Error); _logger.LogException(ex); AfterConnect(); return; } catch (Exception ex) { SendInfo("入力されたURLは存在しないか無効な値です", InfoType.Error); _logger.LogException(ex, "Invalid input", "input=" + input); AfterConnect(); return; } if (string.IsNullOrEmpty(vid)) { AfterConnect(); return; } if (resolver.IsChannel(input) || resolver.IsCustomChannel(input) || resolver.IsUser(input)) { isInputStoringNeeded = true; } _cc = CreateCookieContainer(browserProfile); var userCommentCountDict = CreateUserCommentCountDict(); _connection = CreateConnection(_logger, _cc, _options, _server, _siteOptions, userCommentCountDict, _receivedCommentIds, this, _userStoreManager, SiteContextGuid); _connection.Connected += (s, e) => { Connected?.Invoke(this, new ConnectedEventArgs { IsInputStoringNeeded = isInputStoringNeeded }); }; _connection.MessageReceived += (s, e) => MessageReceived?.Invoke(s, e); _connection.MetadataUpdated += (s, e) => MetadataUpdated?.Invoke(s, e); _connection.LoggedInStateChanged += (s, e) => LoggedInStateChanged?.Invoke(s, e); var reloadManager = new ReloadManager() { CountLimit = 5, CountCheckTimeRangeMin = 1, }; reload: if (_disconnectedByUser) { AfterConnect(); return; } if (!reloadManager.CanReload()) { SendInfo($"{reloadManager.CountCheckTimeRangeMin}分以内に{reloadManager.CountLimit}回再接続を試みました。サーバーに余計な負荷を掛けるのを防ぐため自動再接続を中断します", InfoType.Error); AfterConnect(); return; } reloadManager.SetTime(); try { var disconnectReason = await _connection.ReceiveAsync(vid, browserProfile.Type); switch (disconnectReason) { case DisconnectReason.Reload: SendInfo("エラーが発生したためサーバーとの接続が切断されましたが、自動的に再接続します", InfoType.Error); goto reload; case DisconnectReason.ByUser: case DisconnectReason.Finished: //TODO:SendInfo() break; case DisconnectReason.ChatUnavailable: SendInfo("この配信ではチャットが無効になっているようです", InfoType.Error); break; case DisconnectReason.YtInitialDataNotFound: SendInfo("ytInitialDataの取得に失敗しました", InfoType.Error); break; case DisconnectReason.ServerError: SendInfo("サーバでエラーが発生したため接続できませんでした", InfoType.Error); break; } } catch (Exception ex) { _logger.LogException(ex, "", $"input={input}"); SendInfo("回復不能なエラーが発生しました", InfoType.Error); } AfterConnect(); }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { CanConnect = false; CanDisconnect = true; _disconnectReason = DisconnectReason.Unknown; InputType inputType; try { while (true) { _first.Reset(); string liveId; if (Tools.IsValidUserId(input)) { inputType = InputType.UserId; var userId = Tools.ExtractUserId(input); liveId = await GetLiveIdAsync(userId);//TODO: //GetLiveIdAsync()を実行中にユーザがDisconnect()するとliveIdがnullになる if (string.IsNullOrEmpty(liveId)) { break; } } else if (Tools.IsValidLiveId(input)) { inputType = InputType.LiveId; liveId = Tools.ExtractLiveId(input); } else { inputType = InputType.Unknown; // break; } var liveInfo = await Api.GetLiveInfo(_server, liveId); MetadataUpdated?.Invoke(this, LiveInfo2Meta(liveInfo)); Connected?.Invoke(this, new ConnectedEventArgs { IsInputStoringNeeded = false, UrlToRestore = null, }); var initialComments = await Api.GetLiveComments(_server, liveId); foreach (var c in initialComments) { var userId = c.UserId; var isFirstComment = _first.IsFirstComment(userId); var user = GetUser(userId); var context = CreateMessageContext(c, true, ""); MessageReceived?.Invoke(this, context); } _provider = CreateMessageProvider(liveInfo.Broadcastkey); _provider.MessageReceived += Provider_MessageReceived; _provider.MetadataUpdated += Provider_MetadataUpdated; var commentTask = _provider.ReceiveAsync(); var metaProvider = new MetadataProvider(_server, _siteOptions, liveId); metaProvider.MetadataUpdated += MetaProvider_MetadataUpdated; var metaTask = metaProvider.ReceiveAsync(); var tasks = new List <Task>(); tasks.Add(commentTask); tasks.Add(metaTask); while (tasks.Count > 0) { var t = await Task.WhenAny(tasks); if (t == commentTask) { try { await commentTask; } catch (Exception ex) { _logger.LogException(ex, "", $"input={input}"); } tasks.Remove(commentTask); metaProvider.Disconnect(); try { await metaTask; } catch (Exception ex) { _logger.LogException(ex, "", $"input={input}"); } tasks.Remove(metaTask); } else if (t == metaTask) { try { await metaTask; } catch (Exception ex) { _logger.LogException(ex, "", $"input={input}"); } tasks.Remove(metaTask); //MetadataProviderの内部でcatchしないような例外が投げられた。メタデータの取得は諦めたほうが良い。多分。 } } //inputTypeがUserIdの場合は if (inputType != InputType.UserId) { break; } if (_disconnectReason == DisconnectReason.User) { break; } } } catch (Exception ex) { _logger.LogException(ex); } finally { CanConnect = true; CanDisconnect = false; } }
private void ActiveCounter_Updated(object sender, int e) { MetadataUpdated?.Invoke(this, new Metadata { Active = e.ToString() }); }
/// <summary> /// /// </summary> /// <returns></returns> public async Task <DisconnectReason> ReceiveAsync(string vid, BrowserType browserType) { _disconnectReason = DisconnectReason.Unknown; string liveChatHtml = await GetLiveChatHtml(vid); string ytInitialData; try { ytInitialData = Tools.ExtractYtInitialDataFromLiveChatHtml(liveChatHtml); } catch (ParseException ex) { _logger.LogException(ex, "live_chatからのytInitialDataの抜き出しに失敗", liveChatHtml); return(DisconnectReason.YtInitialDataNotFound); } IContinuation initialContinuation; List <CommentData> initialCommentData; try { (initialContinuation, initialCommentData) = Tools.ParseYtInitialData(ytInitialData); } catch (YouTubeLiveServerErrorException ex) { _logger.LogException(ex, "サーバエラー", $"ytInitialData={ytInitialData},vid={vid}"); return(DisconnectReason.ServerError); } catch (ContinuationNotExistsException) { //放送終了 return(DisconnectReason.Finished); } catch (ChatUnavailableException) { //SendInfo("この配信ではチャットが無効になっているようです", InfoType.Error); return(DisconnectReason.ChatUnavailable); } catch (Exception ex) { _logger.LogException(ex, "未知の例外", $"ytInitialData={ytInitialData},vid={vid}"); return(DisconnectReason.Unknown); } Connected?.Invoke(this, EventArgs.Empty); //直近の過去コメントを送る。 foreach (var data in initialCommentData) { if (_receivedCommentIds.Contains(data.Id)) { continue; } else { _receivedCommentIds.Add(data.Id); } var messageContext = CreateMessageContext(data, true); MessageReceived?.Invoke(this, messageContext); } //コメント投稿に必要なものの準備 PrepareForPostingComments(liveChatHtml, ytInitialData); var tasks = new List <Task>(); Task activeCounterTask = null; IActiveCounter <string> activeCounter = null; if (_options.IsActiveCountEnabled) { activeCounter = new ActiveCounter <string>() { CountIntervalSec = _options.ActiveCountIntervalSec, MeasureSpanMin = _options.ActiveMeasureSpanMin, }; activeCounter.Updated += (s, e) => { MetadataUpdated?.Invoke(this, new Metadata { Active = e.ToString() }); }; activeCounterTask = activeCounter.Start(); tasks.Add(activeCounterTask); } IMetadataProvider metaProvider = null; var metaTask = CreateMetadataReceivingTask(ref metaProvider, browserType, vid, liveChatHtml); if (metaTask != null) { tasks.Add(metaTask); } _chatProvider = new ChatProvider(_server, _logger); _chatProvider.ActionsReceived += (s, e) => { foreach (var action in e) { if (_receivedCommentIds.Contains(action.Id)) { continue; } else { activeCounter?.Add(action.Id); _receivedCommentIds.Add(action.Id); } var messageContext = CreateMessageContext(action, false); MessageReceived?.Invoke(this, messageContext); } }; _chatProvider.InfoReceived += (s, e) => { SendInfo(e.Comment, e.Type); }; var chatTask = _chatProvider.ReceiveAsync(vid, initialContinuation, _cc); tasks.Add(chatTask); _disconnectReason = DisconnectReason.Finished; while (tasks.Count > 0) { var t = await Task.WhenAny(tasks); if (t == metaTask) { try { await metaTask; } catch (Exception ex) { _logger.LogException(ex, "metaTaskが終了した原因"); } //metaTask内でParseExceptionもしくはDisconnect() //metaTaskは終わっても良い。 tasks.Remove(metaTask); metaProvider = null; } else if (t == activeCounterTask) { try { await activeCounterTask; } catch (Exception ex) { _logger.LogException(ex, "activeCounterTaskが終了した原因"); } tasks.Remove(activeCounterTask); activeCounter = null; } else //chatTask { tasks.Remove(chatTask); try { await chatTask; } catch (ReloadException ex) { _logger.LogException(ex, "", $"vid={vid}"); _disconnectReason = DisconnectReason.Reload; } catch (Exception ex) { _logger.LogException(ex); _disconnectReason = DisconnectReason.Unknown; } _chatProvider = null; //chatTaskが終わったらmetaTaskも終了させる metaProvider?.Disconnect(); if (metaTask != null) { try { await metaTask; } catch (Exception ex) { _logger.LogException(ex, "metaTaskが終了した原因"); } tasks.Remove(metaTask); } metaProvider = null; activeCounter?.Stop(); if (activeCounterTask != null) { try { await activeCounterTask; } catch (Exception ex) { _logger.LogException(ex, "activeCounterTaskが終了した原因"); } tasks.Remove(activeCounterTask); } activeCounter = null; } } return(_disconnectReason); }
private void _rpc_InitCompleted(RPCController sender) { if (_disposing) { return; } if (Devices.Count == 0) { Reload(); } else { bool devicesDeleted = false; bool newDevices = false; List <Variable> updatedVariables = Devices.UpdateVariables(_rpc.GetAllValues(), out devicesDeleted, out newDevices); foreach (Variable variable in updatedVariables) { if (!Devices.ContainsKey(variable.PeerID)) { continue; } Device device = Devices[variable.PeerID]; if (!device.Channels.ContainsKey(variable.Channel)) { continue; } DeviceVariableUpdated?.Invoke(this, device, device.Channels[variable.Channel], variable, "HomegearLib.NET"); } bool systemVariablesAdded = false; bool systemVariablesDeleted = false; List <SystemVariable> updatedSystemVariables = SystemVariables.Update(out systemVariablesDeleted, out systemVariablesAdded); foreach (SystemVariable variable in updatedSystemVariables) { SystemVariableUpdated?.Invoke(this, variable); } if ((devicesDeleted || newDevices) && ReloadRequired != null) { ReloadRequired(this, ReloadType.Full); } else { if ((systemVariablesAdded || systemVariablesDeleted) && ReloadRequired != null) { System.Diagnostics.Debug.Write("Position 3"); ReloadRequired(this, ReloadType.SystemVariables); } foreach (KeyValuePair <long, Device> devicePair in Devices) { if (devicePair.Value.MetadataRequested) { bool variablesAdded = false; bool variablesDeleted = false; List <MetadataVariable> updatedMetadata = devicePair.Value.Metadata.Update(out variablesDeleted, out variablesAdded); foreach (MetadataVariable variable in updatedMetadata) { MetadataUpdated?.Invoke(this, devicePair.Value, variable); } if (variablesAdded || variablesDeleted) { DeviceReloadRequired?.Invoke(this, devicePair.Value, null, DeviceReloadType.Metadata); } } } } } }
public async Task ConnectAsync(string input, IBrowserProfile browserProfile) { _blocker.Reset(); var cc = GetCookieContainer(browserProfile); var list = GetCommentProviderInternals(_options, _siteOptions, _userStoreManager, _dataSource, _logger, this, SiteContextGuid); var cu = await GetCurrentUserInfo(browserProfile); if (cu.IsLoggedIn) { foreach (var f in list) { var isValid = f.IsValidInput(input); if (isValid) { _internal = f; break; } } } else { //未ログインでもWebSocket経由なら取れる。 var f = new NewLiveInternalProvider(_options, _siteOptions, _userStoreManager, _logger, _dataSource) { SiteContextGuid = SiteContextGuid, }; var isValid = f.IsValidInput(input); if (isValid) { _internal = f; } } if (_internal == null) { //非対応のInput //AfterDisconnected(); return; } BeforeConnect(); _internal.MetadataUpdated += (s, e) => MetadataUpdated?.Invoke(s, e); _internal.MessageReceived += (s, e) => { if (e.Message is INicoComment nicoComment) { var userId = nicoComment.UserId; var comment = nicoComment.Text; var postedDate = nicoComment.PostedAt; if (!_blocker.IsUniqueComment(userId, comment, postedDate)) { Debug.WriteLine("ニコ生で二重コメントを発見したため無視します"); return; } } MessageReceived?.Invoke(s, e); }; try { await _internal.ConnectAsync(input, cc); } catch (Exception ex) { throw new NicoException("", $"input={input},browser={browserProfile.Type}({browserProfile.ProfileName})", ex); } finally { _internal.MetadataUpdated -= (s, e) => MetadataUpdated?.Invoke(s, e); _internal.MessageReceived -= (s, e) => MessageReceived?.Invoke(s, e); AfterDisconnected(); } }
private void Provider_MetadataUpdated(object sender, IMetadata e) { MetadataUpdated?.Invoke(this, e); }
protected void RaiseMetadataUpdated(IMetadata metadata) { MetadataUpdated?.Invoke(this, metadata); }
//protected virtual Extract() private async Task ConnectInternalAsync(string input, IBrowserProfile browserProfile) { if (_ws != null) { throw new InvalidOperationException(""); } var cookies = GetCookies(browserProfile); _cc = CreateCookieContainer(cookies); _context = Tools.GetContext(cookies); string liveId; try { liveId = await GetLiveId(input); _liveId = liveId; } catch (InvalidInputException ex) { _logger.LogException(ex, "無効な入力値", $"input={input}"); SendSystemInfo("無効な入力値です", InfoType.Error); AfterDisconnected(); return; } var movieContext2 = await GetMovieInfo(liveId); var movieId = movieContext2.MovieId; if (movieId == 0) { SendSystemInfo("存在しないURLまたはIDです", InfoType.Error); AfterDisconnected(); return; } if (movieContext2.OnairStatus == 2) { SendSystemInfo("この放送は終了しています", InfoType.Error); AfterDisconnected(); return; } MetadataUpdated?.Invoke(this, new Metadata { Title = movieContext2.Title }); _startAt = movieContext2.StartedAt.DateTime; _500msTimer.Enabled = true; var(chats, raw) = await GetChats(movieContext2); try { foreach (var item in chats) { var comment = Tools.Parse(item); var commentData = Tools.CreateCommentData(comment, _startAt, _siteOptions); var messageContext = CreateMessageContext(comment, commentData, true); if (messageContext != null) { MessageReceived?.Invoke(this, messageContext); } } } catch (Exception ex) { _logger.LogException(ex, "", "raw=" + raw); } foreach (var user in _userStoreManager.GetAllUsers(SiteType.Openrec)) { if (!(user is IUser2 user2)) { continue; } _userDict.AddOrUpdate(user2.UserId, user2, (id, u) => u); } Reconnect: _ws = CreateOpenrecWebsocket(); _ws.Received += WebSocket_Received; var userAgent = GetUserAgent(browserProfile.Type); var wsTask = _ws.ReceiveAsync(movieId.ToString(), userAgent, cookies); var blackListProvider = CreateBlacklistProvider(); blackListProvider.Received += BlackListProvider_Received; var blackTask = blackListProvider.ReceiveAsync(movieId.ToString(), _context); var tasks = new List <Task> { wsTask, blackTask }; while (tasks.Count > 0) { var t = await Task.WhenAny(tasks); if (t == blackTask) { try { await blackTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(blackTask); } else { blackListProvider.Disconnect(); try { await blackTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(blackTask); SendSystemInfo("ブラックリストタスク終了", InfoType.Debug); try { await wsTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(wsTask); SendSystemInfo("wsタスク終了", InfoType.Debug); } } _ws.Received -= WebSocket_Received; blackListProvider.Received -= BlackListProvider_Received; //意図的な切断では無い場合、配信がまだ続いているか確認して、配信中だったら再接続する。 //2019/03/12 heartbeatを送っているのにも関わらずwebsocketが切断されてしまう場合を確認。ブラウザでも配信中に切断されて再接続するのを確認済み。 if (!_isExpectedDisconnect) { var movieInfo = await GetMovieInfo(liveId); if (movieInfo.OnairStatus == 1) { goto Reconnect; } } }