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