/// <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); }