Ejemplo n.º 1
0
        private LivestreamModel ConvertToLivestreamModel(ExternalAPIs.Hitbox.Dto.Livestream livestream)
        {
            var existingChannel = moniteredChannels.FirstOrDefault(x => x.ChannelId.IsEqualTo(livestream.Channel?.UserName));
            var livestreamModel = new LivestreamModel(livestream.Channel?.UserName, existingChannel ?? new ChannelIdentifier(this, livestream.Channel?.UserName));

            livestreamModel.DisplayName         = livestream.MediaDisplayName;
            livestreamModel.Description         = livestream.MediaStatus?.Trim();
            livestreamModel.Game                = livestream.CategoryName;
            livestreamModel.BroadcasterLanguage = livestream.MediaCountries?.FirstOrDefault()?.ToLower();
            livestreamModel.ThumbnailUrls       = new ThumbnailUrls()
            {
                Medium = StaticContentPrefixUrl + livestream.MediaThumbnail,
                Small  = StaticContentPrefixUrl + livestream.MediaThumbnail,
                Large  = StaticContentPrefixUrl + livestream.MediaThumbnailLarge
            };

            if (livestream.MediaIsLive)
            {
                livestreamModel.Viewers   = livestream.MediaViews;
                livestreamModel.StartTime = livestream.MediaLiveSince ?? DateTimeOffset.Now;
                livestreamModel.Live      = true;
            }
            else
            {
                livestreamModel.Offline();
            }

            return(livestreamModel);
        }
        public async Task <List <LivestreamQueryResult> > GetTopStreams(TopStreamQuery topStreamQuery)
        {
            if (topStreamQuery == null)
            {
                throw new ArgumentNullException(nameof(topStreamQuery));
            }

            var query = new GetStreamsQuery()
            {
                First = topStreamQuery.Take
            };

            if (!string.IsNullOrWhiteSpace(topStreamQuery.GameName))
            {
                var gameId = await GetGameIdByName(topStreamQuery.GameName);

                query.GameIds.Add(gameId);
            }

            var topStreams = await twitchTvHelixClient.GetStreams(query);

            return(topStreams.Select(x =>
            {
                var channelIdentifier = new ChannelIdentifier(this, x.UserId)
                {
                    DisplayName = x.UserName
                };
                var queryResult = new LivestreamQueryResult(channelIdentifier);
                var livestreamModel = new LivestreamModel(x.UserId, channelIdentifier);
                livestreamModel.PopulateWithStreamDetails(x);

                queryResult.LivestreamModel = livestreamModel;
                return queryResult;
            }).ToList());
        }
        private void LoadLivestreams()
        {
            if (initialised)
            {
                return;
            }
            foreach (var channelIdentifier in fileHandler.LoadFromDisk())
            {
                channelIdentifiers.Add(channelIdentifier);
                channelIdentifier.ApiClient.AddChannelWithoutQuerying(channelIdentifier);

                // the channel id will have to be replaced when the livestream is queried the first time
                var livestreamModel = new LivestreamModel(channelIdentifier.ChannelId, channelIdentifier);
                // give livestreams some initial displayname before they have been queried
                if (livestreamModel.ApiClient.ApiName == YoutubeApiClient.API_NAME)
                {
                    livestreamModel.DisplayName = "Youtube Channel: " + channelIdentifier.ChannelId;
                }
                else
                {
                    livestreamModel.DisplayName = channelIdentifier.ChannelId;
                }

                livestreamModel.SetLivestreamNotifyState(settingsHandler.Settings);
                followedLivestreams.Add(livestreamModel);
                livestreamModel.PropertyChanged += LivestreamModelOnPropertyChanged;
            }

            followedLivestreams.CollectionChanged += FollowedLivestreamsOnCollectionChanged;
            initialised = true;
        }
 private void SetViewerCount(LivestreamModel livestreamModel, int viewers)
 {
     livestreamModel.Viewers = viewers;
     if (viewers <= 0)
     {
         livestreamModel.Offline();
     }
 }
Ejemplo n.º 5
0
        public static void SetStreamOffline(LivestreamModel livestreamModel)
        {
            if (livestreamModel == null)
            {
                throw new ArgumentNullException(nameof(livestreamModel));
            }

            livestreamModel.Live        = false;
            livestreamModel.Viewers     = 0;
            livestreamModel.StartTime   = DateTimeOffset.MinValue;
            livestreamModel.Game        = "Offline Game";
            livestreamModel.Description = "Time for bed";
        }
Ejemplo n.º 6
0
        public static void SetStreamOnline(LivestreamModel livestreamModel)
        {
            if (livestreamModel == null)
            {
                throw new ArgumentNullException(nameof(livestreamModel));
            }

            livestreamModel.Live        = true;
            livestreamModel.Viewers     = GetRandomViewerCount();
            livestreamModel.StartTime   = GetRandomStreamStartTime();
            livestreamModel.Game        = "Online Game";
            livestreamModel.Description = "Doing something online right now!";
        }
        public void ToggleOnline()
        {
            if (toggleModel == null)
            {
                toggleModel = monitorStreamsModel.Livestreams.First(x => x.Live);
            }

            if (toggleModel.Live)
            {
                FakeMonitorStreamsModel.SetStreamOffline(toggleModel);
            }
            else
            {
                FakeMonitorStreamsModel.SetStreamOnline(toggleModel);
            }
        }
Ejemplo n.º 8
0
        public static void SetLivestreamNotifyState(this LivestreamModel livestreamModel, Settings settings)
        {
            if (livestreamModel == null)
            {
                return;
            }
            if (string.IsNullOrWhiteSpace(livestreamModel.Id))
            {
                throw new ArgumentNullException(nameof(LivestreamModel.Id));
            }
            if (livestreamModel.ApiClient == null)
            {
                throw new ArgumentNullException(nameof(LivestreamModel.ApiClient));
            }

            livestreamModel.DontNotify = settings.ExcludeFromNotifying.Any(x => Equals(x, livestreamModel.ToExcludeNotify()));
        }
Ejemplo n.º 9
0
        public async Task <List <LivestreamQueryResult> > AddChannel(ChannelIdentifier newChannel)
        {
            if (newChannel == null)
            {
                throw new ArgumentNullException(nameof(newChannel));
            }

            // shorter implementation of QueryChannels
            var queryResults  = new List <LivestreamQueryResult>();
            var onlineStreams = await twitchTvClient.GetStreamsDetails(new[] { newChannel.ChannelId });

            var onlineStream = onlineStreams.FirstOrDefault();

            if (onlineStream != null)
            {
                var livestream = new LivestreamModel(onlineStream.Channel?.Name, newChannel);
                livestream.PopulateWithChannel(onlineStream.Channel);
                livestream.PopulateWithStreamDetails(onlineStream);
                queryResults.Add(new LivestreamQueryResult(newChannel)
                {
                    LivestreamModel = livestream
                });
            }

            // we always need to check for offline channels when attempting to add a channel for the first time
            // this is the only way to detect non-existant/banned channels
            var offlineStreams = await GetOfflineStreamQueryResults(new[] { newChannel }, CancellationToken.None);

            if (onlineStream == null || offlineStreams.Any(x => !x.IsSuccess))
            {
                queryResults.AddRange(offlineStreams);
            }
            else
            {
                offlineStreams.Clear();
            }

            if (queryResults.All(x => x.IsSuccess))
            {
                moniteredChannels.Add(newChannel);
                offlineQueryResultsCache.AddRange(offlineStreams.Where(x => x.IsSuccess));
            }
            return(queryResults);
        }
Ejemplo n.º 10
0
        public async Task <List <LivestreamQueryResult> > GetTopStreams(TopStreamQuery topStreamQuery)
        {
            if (topStreamQuery == null)
            {
                throw new ArgumentNullException(nameof(topStreamQuery));
            }
            var topStreams = await twitchTvClient.GetTopStreams(topStreamQuery);

            return(topStreams.Select(x =>
            {
                var channelIdentifier = new ChannelIdentifier(this, x.Channel.Name);
                var queryResult = new LivestreamQueryResult(channelIdentifier);
                var livestreamModel = new LivestreamModel(x.Channel?.Name, channelIdentifier);
                livestreamModel.PopulateWithStreamDetails(x);
                livestreamModel.PopulateWithChannel(x.Channel);

                queryResult.LivestreamModel = livestreamModel;
                return queryResult;
            }).ToList());
        }
Ejemplo n.º 11
0
        public FakeMonitorStreamsModel()
        {
            Livestreams = new BindableCollection <LivestreamModel>();

            for (int i = 0; i < 10; i++)
            {
                var livestream = new LivestreamModel();
                livestream.DisplayName = "Livestream " + i;

                if (i < 3)
                {
                    SetStreamOnline(livestream);
                }
                else
                {
                    SetStreamOffline(livestream);
                }

                Livestreams.Add(livestream);
            }
        }
 private void UnhookLiveStreamEvents(LivestreamModel livestream)
 {
     livestream.PropertyChanged -= LivestreamOnPropertyChanged;
 }
Ejemplo n.º 13
0
        public async Task <List <LivestreamQueryResult> > QueryChannels(CancellationToken cancellationToken)
        {
            var queryResults = new List <LivestreamQueryResult>();

            if (moniteredChannels.Count == 0)
            {
                return(queryResults);
            }

            // Twitch "get streams" call only returns online streams so to determine if the stream actually exists/is still valid, we must specifically ask for channel details.
            List <Stream> onlineStreams = new List <Stream>();

            int retryCount = 0;

            while (onlineStreams.Count == 0 && !cancellationToken.IsCancellationRequested && retryCount < 3)
            {
                try
                {
                    onlineStreams = await twitchTvClient.GetStreamsDetails(moniteredChannels.Select(x => x.ChannelId), cancellationToken);
                }
                catch (HttpRequestWithStatusException ex) when(ex.StatusCode == HttpStatusCode.ServiceUnavailable)
                {
                    await Task.Delay(2000, cancellationToken);
                }

                retryCount++;
            }

            foreach (var onlineStream in onlineStreams)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(queryResults);
                }

                var channelIdentifier = moniteredChannels.First(x => x.ChannelId.IsEqualTo(onlineStream.Channel?.Name));
                var livestream        = new LivestreamModel(onlineStream.Channel?.Name, channelIdentifier);
                livestream.PopulateWithChannel(onlineStream.Channel);
                livestream.PopulateWithStreamDetails(onlineStream);
                queryResults.Add(new LivestreamQueryResult(channelIdentifier)
                {
                    LivestreamModel = livestream
                });

                // remove cached offline query result if it exists
                var cachedOfflineResult = offlineQueryResultsCache.FirstOrDefault(x => x.ChannelIdentifier.Equals(livestream.ChannelIdentifier));
                if (cachedOfflineResult != null)
                {
                    offlineQueryResultsCache.Remove(cachedOfflineResult);
                }
            }

            // As offline stream querying is expensive due to no bulk call, we only do it once for the majority of streams per application run.
            var offlineChannels = moniteredChannels.Where(x => onlineStreams.All(y => !y.Channel.Name.IsEqualTo(x.ChannelId))).ToList();

            if (queryAllStreams)
            {
                var offlineStreams = await GetOfflineStreamQueryResults(offlineChannels, cancellationToken);

                // only treat offline streams as being queried if no cancel occurred
                if (!cancellationToken.IsCancellationRequested)
                {
                    offlineQueryResultsCache.AddRange(offlineStreams);
                    queryAllStreams = false;
                }
            }
            else // we also need to query stream information for streams which have gone offline since our last query
            {
                var newlyOfflineStreams = offlineChannels.Except(offlineQueryResultsCache.Select(x => x.ChannelIdentifier)).ToList();
                if (newlyOfflineStreams.Any())
                {
                    var offlineStreams = await GetOfflineStreamQueryResults(newlyOfflineStreams, cancellationToken);

                    if (!cancellationToken.IsCancellationRequested)
                    {
                        offlineQueryResultsCache.AddRange(offlineStreams);
                    }
                }
            }

            foreach (var offlineQueryResult in offlineQueryResultsCache.Except(queryResults).Where(x => x.IsSuccess))
            {
                offlineQueryResult.LivestreamModel.Offline();
            }

            queryResults.AddRange(offlineQueryResultsCache);
            return(queryResults);
        }
        private async Task <List <LivestreamModel> > GetLivestreamModels(ChannelIdentifier channelIdentifier, List <string> videoIds, CancellationToken cancellationToken)
        {
            var livestreamModels = new List <LivestreamModel>();

            foreach (var videoId in videoIds)
            {
                LiveStreamingDetails livestreamDetails = null;
                VideoRoot            videoRoot         = null;

                int retryCount = 0;
                while (retryCount < 3 && livestreamDetails == null)
                {
                    if (cancellationToken.IsCancellationRequested)
                    {
                        return(livestreamModels);
                    }

                    try
                    {
                        videoRoot = await youtubeClient.GetLivestreamDetails(videoId, cancellationToken);

                        livestreamDetails = videoRoot.Items?.FirstOrDefault()?.LiveStreamingDetails;
                    }
                    catch (HttpRequestWithStatusException ex) when(ex.StatusCode == HttpStatusCode.ServiceUnavailable)
                    {
                        await Task.Delay(2000, cancellationToken);
                    }
                    catch (HttpRequestWithStatusException)
                    {
                        // can happen in the case of the video being removed
                        // the youtube api will report the videoid as live but looking up the videoid will fail with BadRequest
                        break;
                    }
                    retryCount++;
                }

                if (livestreamDetails == null)
                {
                    continue;
                }

                var snippet = videoRoot.Items?.FirstOrDefault()?.Snippet;
                if (snippet == null)
                {
                    continue;
                }

                var livestreamModel = new LivestreamModel(videoId, channelIdentifier)
                {
                    Live = snippet.LiveBroadcastContent != "none"
                };
                if (!livestreamModel.Live)
                {
                    continue;
                }

                livestreamModel.DisplayName   = snippet.ChannelTitle;
                livestreamModel.Description   = snippet.Title?.Trim();
                livestreamModel.ThumbnailUrls = new ThumbnailUrls()
                {
                    Small  = snippet.Thumbnails?.Standard?.Url,
                    Large  = snippet.Thumbnails?.High?.Url,
                    Medium = snippet.Thumbnails?.Medium?.Url
                };

                livestreamModel.Viewers = livestreamDetails.ConcurrentViewers;

                if (livestreamDetails.ActualStartTime.HasValue)
                {
                    livestreamModel.StartTime = livestreamDetails.ActualStartTime.Value;
                    livestreamModels.Add(livestreamModel);
                }
            }

            return(livestreamModels);
        }
        public async Task <List <LivestreamQueryResult> > QueryChannels(CancellationToken cancellationToken)
        {
            var queryResults = new List <LivestreamQueryResult>();

            if (moniteredChannels.Count == 0)
            {
                return(queryResults);
            }

            // Twitch "get streams" call only returns online streams so to determine if the stream actually exists/is still valid, we must specifically ask for channel details.
            List <Stream> onlineStreams = new List <Stream>();

            int  retryCount = 0;
            bool success    = false;

            while (!success && !cancellationToken.IsCancellationRequested && retryCount < 3)
            {
                try
                {
                    var query = new GetStreamsQuery();
                    query.UserIds.AddRange(moniteredChannels.Select(x => x.ChannelId));
                    onlineStreams = await twitchTvHelixClient.GetStreams(query, cancellationToken);

                    success = true;
                }
                catch (HttpRequestWithStatusException ex) when(ex.StatusCode == HttpStatusCode.ServiceUnavailable)
                {
                    await Task.Delay(2000, cancellationToken);

                    retryCount++;
                }
            }

            foreach (var onlineStream in onlineStreams)
            {
                if (cancellationToken.IsCancellationRequested)
                {
                    return(queryResults);
                }

                var channelIdentifier = moniteredChannels.First(x => x.ChannelId.IsEqualTo(onlineStream.UserId));
                var gameName          = await GetGameNameById(onlineStream.GameId);

                var livestream = new LivestreamModel(onlineStream.UserId, channelIdentifier);
                livestream.PopulateWithStreamDetails(onlineStream);
                livestream.Game = gameName;
                queryResults.Add(new LivestreamQueryResult(channelIdentifier)
                {
                    LivestreamModel = livestream
                });
            }

            var offlineStreams = moniteredChannels.Where(x => onlineStreams.All(y => y.UserId != x.ChannelId)).ToList();

            foreach (var offlineStream in offlineStreams)
            {
                var queryResult = new LivestreamQueryResult(offlineStream)
                {
                    LivestreamModel = new LivestreamModel(offlineStream.ChannelId, offlineStream)
                    {
                        DisplayName = offlineStream.DisplayName ?? offlineStream.ChannelId
                    }
                };

                queryResults.Add(queryResult);
            }

            return(queryResults);
        }
        public async Task <List <LivestreamQueryResult> > AddChannel(ChannelIdentifier newChannel)
        {
            if (newChannel == null)
            {
                throw new ArgumentNullException(nameof(newChannel));
            }

            // shorter implementation of QueryChannels
            var  queryResults = new List <LivestreamQueryResult>();
            User user;

            if (long.TryParse(newChannel.ChannelId, out var _))
            {
                var users = await twitchTvHelixClient.GetUsers(new GetUsersQuery()
                {
                    UserIds = new List <string>()
                    {
                        newChannel.ChannelId
                    }
                });

                user = users.FirstOrDefault();
            }
            else
            {
                user = await twitchTvHelixClient.GetUserByUsername(newChannel.ChannelId);
            }

            if (user == null)
            {
                throw new InvalidOperationException("No user found for id " + newChannel.ChannelId);
            }

            newChannel.OverrideChannelId(user.Id);
            newChannel.DisplayName      = user.DisplayName;
            channelIdToUserMap[user.Id] = user;
            var livestream = new LivestreamModel(user.Id, newChannel)
            {
                DisplayName = user.DisplayName
            };

            var onlineStreams = await twitchTvHelixClient.GetStreams(new GetStreamsQuery()
            {
                UserIds = new List <string>()
                {
                    user.Id
                }
            });

            var onlineStream = onlineStreams.FirstOrDefault();

            if (onlineStream != null)
            {
                livestream.PopulateWithStreamDetails(onlineStream);
            }

            queryResults.Add(new LivestreamQueryResult(newChannel)
            {
                LivestreamModel = livestream
            });

            if (queryResults.All(x => x.IsSuccess))
            {
                moniteredChannels.Add(newChannel);
            }
            return(queryResults);
        }