private async Task ConnectInternalAsync(IInput input, IBrowserProfile browserProfile) { var resolver = new VidResolver(); var vidResult = await resolver.GetVid(_server, input); string vid; switch (vidResult) { case VidResult single: vid = single.Vid; break; case MultiVidsResult multi: SendSystemInfo("このチャンネルでは複数のライブが配信中です。", InfoType.Notice); foreach (var v in multi.Vids) { SendSystemInfo(v, InfoType.Notice); //titleも欲しい } return; case NoVidResult no: SendSystemInfo("このチャンネルでは生放送をしていないようです", InfoType.Error); return; default: throw new NotImplementedException(); } _cc = CreateCookieContainer(browserProfile); await InitElapsedTimer(vid); _chatProvider = new ChatProvider2(_siteOptions, _logger); _chatProvider.MessageReceived += ChatProvider_MessageReceived; _chatProvider.LoggedInStateChanged += _chatProvider_LoggedInStateChanged; _chatProvider.InfoReceived += ChatProvider_InfoReceived; var metaProvider = new MetaDataYoutubeiProvider(_server, _logger); metaProvider.InfoReceived += MetaProvider_InfoReceived; metaProvider.MetadataReceived += MetaProvider_MetadataReceived; reload: var liveChatHtml = await GetLiveChat(vid, _cc); var liveChat = LiveChat.Parse(liveChatHtml); var ytCfgStr = Tools.ExtractYtCfg(liveChatHtml); //var ytCfg = new YtCfgOld(ytCfgStr); var ytCfg = liveChat.YtCfg; var ytInitialData = Tools.ExtractYtInitialData(liveChatHtml); if (!ytInitialData.CanChat) { SendSystemInfo("このライブストリームではチャットは無効です。", InfoType.Notice); return; } var loginInfo = Tools.CreateLoginInfo(liveChat.YtCfg.IsLoggedIn); //ログイン済みユーザの正常にコメントが取得できるようになったら以下のコードは不要 //---ここから--- if (loginInfo is LoggedIn) { var k = Tools.GetSapiSid(_cc); if (k == null) { //SIDが無い。ログイン済み判定なのにSIDが無い場合が散見されるが原因不明。強制的に未ログインにする。 var cookies = Tools.ExtractCookies(_cc); var cver = ytInitialData.Cver; var keys = string.Join(",", cookies.Select(c => c.Name)); _logger.LogException(new Exception(), "", $"cver={cver},keys={keys}"); _cc = new CookieContainer(); goto reload; } } //---ここまで--- SetLoggedInState(liveChat.YtCfg.IsLoggedIn); _postCommentCoodinator = new DataCreator(ytInitialData, liveChat.YtCfg.InnertubeApiKey, liveChat.YtCfg.DelegatedSessionId, _cc); foreach (var action in liveChat.YtInitialData.Actions) { OnMessageReceived(action, true); } //var initialActions = ytInitialData.GetActions(); //foreach (var action in initialActions) //{ // OnMessageReceived(action, true); //} var chatTask = _chatProvider.ReceiveAsync(vid, liveChat.YtInitialData, ytCfg, _cc, loginInfo); var metaTask = metaProvider.ReceiveAsync(ytCfg, vid, _cc); var tasks = new List <Task> { chatTask, metaTask }; while (tasks.Count > 0) { var t = await Task.WhenAny(tasks); if (t == chatTask) { metaProvider.Disconnect(); try { await metaTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(metaTask); try { await chatTask; } catch (GetLiveChatException ex) { _isDisconnectedExpected = true; string html; try { html = await GetLiveChat(vid, _cc); } catch { html = ""; } _logger.LogException(ex, "", $"input={input.Raw},html={html}"); SendSystemInfo($"エラーが発生したため、これ以上コメントを取得できません{Environment.NewLine}{ex.Message}", InfoType.Notice); } catch (ContinuationNotExistsException) { break; } catch (ChatUnavailableException ex) { _isDisconnectedExpected = true; _logger.LogException(ex); SendSystemInfo("配信が終了したか、チャットが無効です。", InfoType.Notice); } catch (ReloadException) { } catch (SpecChangedException ex) { SendSystemInfo("YouTubeの仕様変更に未対応のためコメント取得を継続できません", InfoType.Error); _logger.LogException(ex); _isDisconnectedExpected = true; } catch (Exception ex) { SendSystemInfo(ex.Message, InfoType.Error); //意図しない切断 //ただし、サーバーからReloadメッセージが来た場合と違って、単純にリロードすれば済む問題ではない。 _logger.LogException(ex); await Task.Delay(1000); } tasks.Remove(chatTask); if (_isDisconnectedExpected == false) { //何らかの原因で意図しない切断が発生した。 SendSystemInfo("エラーが発生したためサーバーとの接続が切断されましたが、自動的に再接続します", InfoType.Notice); goto reload; } } else { try { await metaTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(metaTask); } } _chatProvider.MessageReceived -= ChatProvider_MessageReceived; _chatProvider.LoggedInStateChanged -= _chatProvider_LoggedInStateChanged; _chatProvider.InfoReceived -= ChatProvider_InfoReceived; metaProvider.InfoReceived -= MetaProvider_InfoReceived; metaProvider.MetadataReceived -= MetaProvider_MetadataReceived; }
private async Task ConnectInternalAsync(string input, IBrowserProfile browserProfile) { var resolver = new VidResolver(); var vidResult = await resolver.GetVid(_server, input); string vid; switch (vidResult) { case VidResult single: vid = single.Vid; break; case MultiVidsResult multi: SendSystemInfo("このチャンネルでは複数のライブが配信中です。", InfoType.Notice); foreach (var v in multi.Vids) { SendSystemInfo(v, InfoType.Notice); //titleも欲しい } return; case NoVidResult no: SendSystemInfo("このチャンネルでは生放送をしていないようです", InfoType.Error); return; default: throw new NotImplementedException(); } _cc = CreateCookieContainer(browserProfile); await InitElapsedTimer(vid); _chatProvider = new ChatProvider2(_siteOptions); _chatProvider.MessageReceived += ChatProvider_MessageReceived; _chatProvider.LoggedInStateChanged += _chatProvider_LoggedInStateChanged; _chatProvider.InfoReceived += ChatProvider_InfoReceived; var metaProvider = new MetaDataYoutubeiProvider(_server, _logger); metaProvider.InfoReceived += MetaProvider_InfoReceived; metaProvider.MetadataReceived += MetaProvider_MetadataReceived; reload: var liveChatHtml = await GetLiveChat(vid, _cc); var ytCfgStr = Tools.ExtractYtCfg(liveChatHtml); var ytCfg = new YtCfg(ytCfgStr); var ytInitialData = Tools.ExtractYtInitialData(liveChatHtml); if (!ytInitialData.CanChat) { SendSystemInfo("このライブストリームではチャットは無効です。", InfoType.Notice); return; } var loginInfo = Tools.CreateLoginInfo(ytInitialData.IsLoggedIn); SetLoggedInState(ytInitialData.IsLoggedIn); _postCommentCoodinator = new DataCreator(ytInitialData, ytCfg.InnerTubeApiKey, _cc); var initialActions = ytInitialData.GetActions(); foreach (var action in initialActions) { OnMessageReceived(action, true); } var chatTask = _chatProvider.ReceiveAsync(vid, ytInitialData, ytCfg, _cc, loginInfo); var metaTask = metaProvider.ReceiveAsync(ytCfg, vid, _cc); var tasks = new List <Task> { chatTask, metaTask }; while (tasks.Count > 0) { var t = await Task.WhenAny(tasks); if (t == chatTask) { metaProvider.Disconnect(); try { await metaTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(metaTask); try { await chatTask; } catch (ChatUnavailableException) { _isDisconnectedExpected = true; SendSystemInfo("配信が終了したか、チャットが無効です。", InfoType.Notice); } catch (ReloadException) { } catch (SpecChangedException ex) { SendSystemInfo("YouTubeの仕様変更に未対応のためコメント取得を継続できません", InfoType.Error); _logger.LogException(ex); _isDisconnectedExpected = true; } catch (Exception ex) { SendSystemInfo(ex.Message, InfoType.Error); //意図しない切断 //ただし、サーバーからReloadメッセージが来た場合と違って、単純にリロードすれば済む問題ではない。 _logger.LogException(ex); await Task.Delay(1000); } tasks.Remove(chatTask); if (_isDisconnectedExpected == false) { //何らかの原因で意図しない切断が発生した。 SendSystemInfo("エラーが発生したためサーバーとの接続が切断されましたが、自動的に再接続します", InfoType.Notice); goto reload; } } else { try { await metaTask; } catch (Exception ex) { _logger.LogException(ex); } tasks.Remove(metaTask); } } _chatProvider.MessageReceived -= ChatProvider_MessageReceived; _chatProvider.LoggedInStateChanged -= _chatProvider_LoggedInStateChanged; _chatProvider.InfoReceived -= ChatProvider_InfoReceived; metaProvider.InfoReceived -= MetaProvider_InfoReceived; metaProvider.MetadataReceived -= MetaProvider_MetadataReceived; }