Beispiel #1
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);
        }
Beispiel #2
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());
        }
Beispiel #3
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);
        }