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); }
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()); }
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); }