Example #1
0
        /// <summary>
        ///
        /// </summary>
        /// <returns></returns>
        public async Task <DisconnectReason> ReceiveAsync(string vid, BrowserType browserType)
        {
            _disconnectReason = DisconnectReason.Unknown;
            string liveChatHtml = await GetLiveChatHtml(vid);

            string ytInitialData = ExtractYtInitialData(liveChatHtml);

            if (string.IsNullOrEmpty(ytInitialData))
            {
                //これが無いとコメントが取れないから終了
                //SendInfo("ytInitialDataの取得に失敗しました", InfoType.Error);
                return(DisconnectReason.YtInitialDataNotFound);
            }
            IContinuation      initialContinuation;
            List <CommentData> initialCommentData;

            try
            {
                (initialContinuation, initialCommentData) = Tools.ParseYtInitialData(ytInitialData);
            }
            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();
                    try
                    {
                        await metaTask;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogException(ex, "metaTaskが終了した原因");
                    }
                    tasks.Remove(metaTask);
                    metaProvider = null;

                    activeCounter?.Stop();
                    try
                    {
                        await activeCounterTask;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogException(ex, "activeCounterTaskが終了した原因");
                    }
                    tasks.Remove(activeCounterTask);
                    activeCounter = null;
                }
            }
            return(_disconnectReason);
        }
 protected void RaiseMetadataUpdated(IMetadata metadata)
 {
     MetadataUpdated?.Invoke(this, metadata);
 }
Example #3
0
 private void Apply(MetadataUpdated e)
 {
     Metadata        = e.Metadata;
     UpdatedBy       = e.UserId;
     UpdatedDateTime = e.TimeStamp;
 }
Example #4
0
        //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;
                }
            }
        }
 private void MessageProvider_MetaReceived(object sender, IMetadata e)
 {
     MetadataUpdated?.Invoke(this, e);
 }
 internal void OnMetadataUpdated(EventArgs e)
 {
     MetadataUpdated.RaiseEvent(this, e);
 }
Example #7
0
        private async Task ConnectInternalAsync(string input, IBrowserProfile browserProfile)
        {
            if (string.IsNullOrEmpty(input))
            {
                SendInfo("URLを入力してください", InfoType.Error);
                return;
            }

            MetadataUpdated?.Invoke(this, new Metadata
            {
                Active         = "-",
                CurrentViewers = "-",
                Elapsed        = "-",
                Title          = "-",
                TotalViewers   = "-",
            });

            _cc = CreateCookieContainer(browserProfile);
            var bigoId = GetBigoId(input);

            _webSocketLink = await Api.GetWebSocketLink(_server, _cc);

            _internalStudioInfo = await Api.GetInternalStudioInfo(bigoId, _server, _cc);

            var gifts = await Api.GetOnlineGift(DateTime.Now, _server, _cc);

            _giftDict = gifts.ToDictionary(kv => kv.Typeid);
            var title = await GetTitleAsync(bigoId, _cc);

            if (!string.IsNullOrEmpty(title))
            {
                MetadataUpdated?.Invoke(this, new Metadata
                {
                    Title = title,
                });
            }
            var websocketUrl = "wss://wss.bigolive.tv/bigo/web";

            messageProvider = new MessageProvider(new Websocket
            {
                EnableAutoSendPing   = true,
                AutoSendPingInterval = 1000,
                NoDelay = true,
            }, new MessageParser());
            messageProvider.Opened   += MessageProvider_Opened;
            messageProvider.Received += MessageProvider_Received;
            try
            {
reload:
                await messageProvider.ReceiveAsync(websocketUrl);

                if (!_disconnectedExpected)
                {
                    Debug.WriteLine("BIGO reload!");
                    goto reload;
                }
            }
            catch (Exception ex)
            {
            }
            finally
            {
                messageProvider.Opened   -= MessageProvider_Opened;
                messageProvider.Received -= MessageProvider_Received;
                messageProvider           = null;
            }
        }
        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)
                    {
                        try
                        {
                            await messageProviderTask;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogException(ex);
                        }
                        tasks.Remove(messageProviderTask);
                        try
                        {
                            promptyStatsProvider.Disconnect();
                            await promptyStatsTask;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogException(ex);
                        }
                        tasks.Remove(promptyStatsTask);
                        break;
                    }
                    else if (t == promptyStatsTask)
                    {
                        try
                        {
                            await promptyStatsTask;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogException(ex);
                        }
                    }
                    else if (t == blackListTask)
                    {
                        try
                        {
                            await blackListTask;
                        }
                        catch (Exception ex)
                        {
                            _logger.LogException(ex);
                        }
                    }
                }
                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();
        }