private void NotifyIfLive(LivestreamModel livestreamModel) { if (!hasRefreshed) { return; // dont show notifications for the initial refresh } if (!livestreamModel.Live) { return; // only care about streams coming online } // avoid a twitch api bug where sometimes online streams will not be returned when querying for online streams // the best way we can work around this is to pick a reasonable uptime value after which we will never show online notifications. // we check the LastLiveTime for null to ensure we will always notify the first time a stream come online if (livestreamModel.LastLiveTime != null && livestreamModel.Uptime > TimeSpan.FromMinutes(5)) { return; } var notification = new LivestreamNotification() { Title = $"{livestreamModel.DisplayName} Online", Message = livestreamModel.Description, ImageUrl = livestreamModel.ThumbnailUrls?.Small, LivestreamModel = livestreamModel, }; AddNotification(notification); }
public static LivestreamModel PopulateWithStreamDetails( this LivestreamModel livestreamModel, Stream streamDetails) { if (streamDetails == null) { return(livestreamModel); } livestreamModel.Viewers = streamDetails.Viewers ?? 0; livestreamModel.ThumbnailUrls = new ThumbnailUrls() { Large = streamDetails.Preview.Large, Medium = streamDetails.Preview.Medium, Small = streamDetails.Preview.Small, }; if (streamDetails.CreatedAt != null) { livestreamModel.StartTime = DateTimeOffset.Parse(streamDetails.CreatedAt); livestreamModel.NotifyOfPropertyChange(nameof(livestreamModel.Uptime)); } // need to update other details before flipping the stream to online livestreamModel.Live = streamDetails.Viewers.HasValue; if (streamDetails.Channel != null) { livestreamModel.PopulateWithChannel(streamDetails.Channel); } return(livestreamModel); }
public static LivestreamModel PopulateWithStreamDetails( this LivestreamModel livestreamModel, Stream stream) { if (stream == null) { return(livestreamModel); } livestreamModel.Viewers = stream.ViewerCount; // the values here are what the v5 api returned for large/medium/small var largeThumbnail = stream.ThumbnailTemplateUrl.Replace("{width}", "640").Replace("{height}", "360"); var mediumThumbnail = stream.ThumbnailTemplateUrl.Replace("{width}", "320").Replace("{height}", "180"); var smallThumbnail = stream.ThumbnailTemplateUrl.Replace("{width}", "80").Replace("{height}", "45"); livestreamModel.ThumbnailUrls = new ThumbnailUrls() { Large = largeThumbnail, Medium = mediumThumbnail, Small = smallThumbnail, }; livestreamModel.StartTime = stream.StartedAt; // need to update other details before flipping the stream to online livestreamModel.Live = stream.Type == "live"; livestreamModel.DisplayName = stream.UserName; livestreamModel.Description = stream.Title; return(livestreamModel); }
public async Task OpenStream(LivestreamModel livestreamModel, string streamQuality, IViewAware viewAware) { if (livestreamModel?.ApiClient == null || !livestreamModel.Live) { return; } string livestreamerArgs = $"{livestreamModel.StreamUrl} {streamQuality}"; var apiClient = livestreamModel.ApiClient; // hack to pass through the client id to livestreamer if (settingsHandler.Settings.PassthroughClientId) { if (apiClient is TwitchApiClient) { livestreamerArgs = $"--http-header {RequestConstants.ClientIdHeaderKey}={RequestConstants.ClientIdHeaderValue} {livestreamerArgs}"; } } else { if (!apiClient.IsAuthorized) { await apiClient.Authorize(viewAware); } if (apiClient.IsAuthorized && !string.IsNullOrWhiteSpace(apiClient.LivestreamerAuthorizationArg)) { livestreamerArgs += " " + apiClient.LivestreamerAuthorizationArg; } } var launcher = Path.GetFileName(settingsHandler.Settings.LivestreamerFullPath); var messageBoxViewModel = ShowLivestreamerLoadMessageBox( title: $"Stream '{livestreamModel.DisplayName}'", messageText: $"Launching {settingsHandler.Settings.LivestreamExeDisplayName}....{Environment.NewLine}'{launcher} {livestreamerArgs}'"); // Notify the user if the quality has been swapped back to source due to the livestream not being partenered (twitch specific). if (!livestreamModel.IsPartner && streamQuality != StreamQuality.Best.ToString()) { messageBoxViewModel.MessageText += Environment.NewLine + $"[NOTE] Channel is not a twitch partner so falling back to {StreamQuality.Best} quality"; } lock (WatchingStreamsLock) { watchingStreams.Add(livestreamModel); } StartLivestreamer(livestreamerArgs, streamQuality, messageBoxViewModel, onClose: () => { lock (WatchingStreamsLock) { watchingStreams.Remove(livestreamModel); } }); }
private void HookLivestreamChangeEvents(LivestreamModel newLivestream) { if (newLivestream == null) { throw new ArgumentNullException(nameof(newLivestream)); } newLivestream.PropertyChanged += LivestreamOnPropertyChanged; }
private void UnhookLivestreamChangeEvents(LivestreamModel removedLivestream) { if (removedLivestream == null) { throw new ArgumentNullException(nameof(removedLivestream)); } removedLivestream.PropertyChanged += LivestreamOnPropertyChanged; }
public TopStreamResult() { if (!Execute.InDesignMode) { throw new InvalidOperationException("Design time only constructor"); } LivestreamModel = new LivestreamModel(); ChannelIdentifier = new ChannelIdentifier(); }
public async Task OpenStream(LivestreamModel livestreamModel, IViewAware viewAware) { if (livestreamModel?.ApiClient == null || !livestreamModel.Live) { return; } var favoriteQualities = settingsHandler.Settings.GetStreamQualities(livestreamModel.ApiClient.ApiName); var qualities = favoriteQualities.Qualities.Union(new[] { favoriteQualities.FallbackQuality }); var streamUrl = await livestreamModel.GetStreamUrl; string livestreamerArgs = $"{streamUrl} {string.Join(",", qualities)}"; var apiClient = livestreamModel.ApiClient; // hack to pass through the client id to livestreamer if (settingsHandler.Settings.PassthroughClientId) { if (apiClient is TwitchApiClient) { livestreamerArgs = $"--http-header {RequestConstants.ClientIdHeaderKey}={RequestConstants.ClientIdHeaderValue} {livestreamerArgs}"; } } else { if (!apiClient.IsAuthorized) { await apiClient.Authorize(viewAware); } if (apiClient.IsAuthorized && !string.IsNullOrWhiteSpace(apiClient.LivestreamerAuthorizationArg)) { livestreamerArgs += " " + apiClient.LivestreamerAuthorizationArg; } } var launcher = Path.GetFileName(settingsHandler.Settings.LivestreamerFullPath); var messageBoxViewModel = ShowLivestreamerLoadMessageBox( title: $"Stream '{livestreamModel.DisplayName}'", messageText: $"Launching {settingsHandler.Settings.LivestreamExeDisplayName}....{Environment.NewLine}'{launcher} {livestreamerArgs}'"); lock (WatchingStreamsLock) { watchingStreams.Add(livestreamModel); } StartLivestreamer(livestreamerArgs, messageBoxViewModel, onClose: () => { lock (WatchingStreamsLock) { watchingStreams.Remove(livestreamModel); } }); }
public async Task OpenChat(LivestreamModel livestreamModel, IViewAware fromScreen) { // guard against invalid/missing chrome path var chromeLocation = settingsHandler.Settings.ChromeFullPath; if (string.IsNullOrWhiteSpace(chromeLocation)) { await fromScreen.ShowMessageAsync("No chrome locations specified", $"Chrome location is not set in settings.{Environment.NewLine}Chat relies on chrome to function."); return; } if (!File.Exists(chromeLocation)) { await fromScreen.ShowMessageAsync("Chrome not found", $"Could not find chrome @ {chromeLocation}.{Environment.NewLine}Chat relies on chrome to function."); return; } // guard against stream provider not having chat support if (!livestreamModel.ApiClient.HasChatSupport) { await fromScreen.ShowMessageAsync("Chat not supported", $"No external chat support for stream provider '{livestreamModel.ApiClient.ApiName}'"); return; } string chromeArgs = $"--app={livestreamModel.ChatUrl} --window-size=350,758"; await Task.Run(async() => { try { var proc = new Process { StartInfo = { FileName = chromeLocation, Arguments = chromeArgs, CreateNoWindow = true, UseShellExecute = false } }; proc.Start(); } catch (Exception ex) { await fromScreen.ShowMessageAsync("Error launching chat", ex.Message); } }); }
public TopStreamResult(LivestreamModel livestreamModel, ChannelIdentifier channelIdentifier) { if (livestreamModel == null) { throw new ArgumentNullException(nameof(livestreamModel)); } if (channelIdentifier == null) { throw new ArgumentNullException(nameof(channelIdentifier)); } LivestreamModel = livestreamModel; ChannelIdentifier = channelIdentifier; }
public static void PopulateSelf(this LivestreamModel livestreamModel, LivestreamModel consume) { livestreamModel.BroadcasterLanguage = consume.BroadcasterLanguage; livestreamModel.Description = consume.Description?.Trim(); livestreamModel.DisplayName = consume.DisplayName; livestreamModel.Game = consume.Game; livestreamModel.IsPartner = consume.IsPartner; livestreamModel.Language = consume.Language; livestreamModel.ThumbnailUrls = consume.ThumbnailUrls; livestreamModel.Viewers = consume.Viewers; livestreamModel.StartTime = consume.StartTime; livestreamModel.Live = consume.Live; }
public static void PopulateWithChannel(this LivestreamModel livestreamModel, Channel channel) { if (channel == null) { return; } livestreamModel.DisplayName = channel.DisplayName; livestreamModel.Description = channel.Status?.Trim(); livestreamModel.Game = channel.Game; livestreamModel.IsPartner = channel.Partner.HasValue && channel.Partner.Value; livestreamModel.BroadcasterLanguage = channel.BroadcasterLanguage; livestreamModel.Language = channel.Language; }
public static UniqueStreamKey ToExcludeNotify(this LivestreamModel livestreamModel) { return(new UniqueStreamKey(livestreamModel.ApiClient.ApiName, livestreamModel.Id)); }
public async Task OpenChat(LivestreamModel livestreamModel, IViewAware fromScreen) { // guard against stream provider with unknown chat support if (!livestreamModel.ApiClient.HasChatSupport) { await fromScreen.ShowMessageAsync("Chat not supported", $"No external chat support for stream provider '{livestreamModel.ApiClient.ApiName}'"); return; } if (string.IsNullOrWhiteSpace(settingsHandler.Settings.ChatCommandLine)) { await fromScreen.ShowMessageAsync("No chat command specified", "Chat command is not set in settings."); return; } // guard against chat command that contains no url token if (!settingsHandler.Settings.ChatCommandLine.Contains(Settings.CHAT_URL_REPLACEMENT_TOKEN)) { await fromScreen.ShowMessageAsync("Missing url token in chat command", $"Chat command is missing the url token {Settings.CHAT_URL_REPLACEMENT_TOKEN}."); return; } var command = settingsHandler.Settings.ChatCommandLine.Replace(Settings.CHAT_URL_REPLACEMENT_TOKEN, livestreamModel.ChatUrl); await Task.Run(async() => { try { var proc = new Process { StartInfo = { FileName = "cmd.exe", Arguments = "/c " + command, CreateNoWindow = true, UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true, } }; proc.Start(); string errorOutput = proc.StandardError.ReadToEnd(); proc.WaitForExit(); // check for exit code as well because sometimes processes emit warnings in the error output stream if (proc.ExitCode != 0 && errorOutput != string.Empty) { await Execute.OnUIThreadAsync(async() => await fromScreen.ShowMessageAsync("Error launching chat", errorOutput)); } } catch (Exception ex) { await Execute.OnUIThreadAsync(async() => await fromScreen.ShowMessageAsync("Error launching chat", ex.Message)); } }); }
public TopStreamResult(LivestreamModel livestreamModel, ChannelIdentifier channelIdentifier) { LivestreamModel = livestreamModel ?? throw new ArgumentNullException(nameof(livestreamModel)); ChannelIdentifier = channelIdentifier ?? throw new ArgumentNullException(nameof(channelIdentifier)); }
protected bool Equals(LivestreamModel other) { return(Equals(UniqueStreamKey, other.UniqueStreamKey)); }