Example #1
0
 public static Input.IInput ParseInput(string input)
 {
     if (string.IsNullOrEmpty(input))
     {
         throw new ArgumentNullException(nameof(input));
     }
     if (VidResolver.IsChannel(input))
     {
         return(new Input.ChannelUrl(input));
     }
     else if (VidResolver.IsCustomChannel(input))
     {
         return(new Input.CustomChannelUrl(input));
     }
     else if (VidResolver.IsStudio(input))
     {
         return(new Input.StudioUrl(input));
     }
     else if (VidResolver.IsUser(input))
     {
         return(new Input.UserUrl(input));
     }
     else if (VidResolver.IsVid(input))
     {
         return(new Input.Vid(input));
     }
     else if (VidResolver.IsWatch(input))
     {
         return(new Input.WatchUrl(input));
     }
     return(new Input.InvalidInput(input));
 }
 public WatchUrl(string watchUrl)
 {
     Raw = watchUrl;
     if (!VidResolver.TryWatch(watchUrl, out var vid))
     {
         throw new ArgumentException(nameof(vid));
     }
     Vid = vid;
 }
Example #3
0
        public async Task 短縮URLに対応()
        {
            var s          = new VidResolver();
            var serverMock = new Mock <IYouTubeLibeServer>();
            var result1    = await s.GetVid(serverMock.Object, "https://youtu.be/bexmlC2nD0U");

            Assert.IsTrue(result1 is IVidResult);
            Assert.AreEqual("bexmlC2nD0U", ((VidResult)result1).Vid);
        }
Example #4
0
        public async Task ResolveVidFromChannelUrl()
        {
            var sample     = Tools.GetSampleData("Channel_some_archives.txt");
            var s          = new VidResolver();
            var serverMock = new Mock <IYouTubeLibeServer>();

            serverMock.Setup(k => k.GetEnAsync("https://www.youtube.com/channel/UCkDuaqQxw3k7Aa816kngUYg/videos?flow=list&view=2")).Returns(Task.FromResult(sample));
            var result1 = await s.GetVid(serverMock.Object, "https://www.youtube.com/channel/UCkDuaqQxw3k7Aa816kngUYg");

            Assert.IsTrue(result1 is NoVidResult);
        }
Example #5
0
        public async Task GetVidsFromChannelId_CC_label_Test()
        {
            var html      = Tools.GetSampleData("Channel_some_archives_with_cc_label.txt");
            var server    = new Mock <IYouTubeLibeServer>();
            var channelId = "UCBL9Blq9GDhPGAQbfUJIYXQ";

            server.Setup(s => s.GetEnAsync("https://www.youtube.com/channel/" + channelId + "/videos?flow=list&view=0")).Returns(Task.FromResult(html));
            var resolver = new VidResolver();
            var vids     = await resolver.GetVidsFromChannelId(server.Object, channelId);

            Assert.IsTrue(vids.Count == 0);
        }
Example #6
0
        public async Task ResolveVidFromChanneLivelUrl()
        {
            var sample     = Tools.GetSampleData("Channel_live.txt");
            var s          = new VidResolver();
            var serverMock = new Mock <IYouTubeLibeServer>();

            serverMock.Setup(k => k.GetEnAsync(It.IsAny <string>())).Returns(Task.FromResult(sample));
            var result1 = await s.GetVid(serverMock.Object, "https://www.youtube.com/channel/UCkDuaqQxw3k7Aa816kngUYg") as VidResult;

            Assert.IsTrue(result1 is VidResult);
            Assert.AreEqual("klvzbBP7zM8", ((VidResult)result1).Vid);
        }
Example #7
0
        public async Task GetVidsFromChannelId_on_air_Test()
        {
            var html      = Tools.GetSampleData("Channel_on_air.txt");
            var server    = new Mock <IYouTubeLibeServer>();
            var channelId = "UCBL9Blq9GDhPGAQbfUJIYXQ";

            server.Setup(s => s.GetEnAsync("https://www.youtube.com/channel/" + channelId + "/videos?flow=list&view=0")).Returns(Task.FromResult(html));
            var resolver = new VidResolver();
            var vids     = await resolver.GetVidsFromChannelId(server.Object, channelId);

            Assert.IsTrue(vids.Count == 1);
            Assert.AreEqual("AuFOOUtIyUY", vids[0]);
        }
Example #8
0
        public async Task ResolveVidFromWatchUrl()
        {
            var s          = new VidResolver();
            var serverMock = new Mock <IYouTubeLibeServer>();
            var result1    = await s.GetVid(serverMock.Object, "https://www.youtube.com/watch?v=Rs-WxTGgVus");

            Assert.IsTrue(result1 is IVidResult);
            Assert.AreEqual("Rs-WxTGgVus", ((VidResult)result1).Vid);

            var result2 = await s.GetVid(serverMock.Object, "https://www.youtube.com/watch?v=Rs-WxTGgVus&feature=test");

            Assert.IsTrue(result2 is IVidResult);
            Assert.AreEqual("Rs-WxTGgVus", ((VidResult)result2).Vid);
        }
 public UserUrl(string userUrl)
 {
     Raw    = userUrl;
     UserId = VidResolver.ExtractUserId(userUrl);
 }
 public ChannelUrl(string channelUrl)
 {
     Raw       = channelUrl;
     ChannelId = VidResolver.ExtractChannelId(channelUrl);
 }
        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;
        }
 public CustomChannelUrl(string customChannelUrl)
 {
     Raw             = customChannelUrl;
     CustomChannelId = VidResolver.ExtractCustomChannelId(customChannelUrl);
 }
 public StudioUrl(string studioUrl)
 {
     Raw = studioUrl;
     Vid = VidResolver.ExtractVidFromStudioUrl(studioUrl);
 }
Example #14
0
        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;
        }