Skip to content

Commit

Permalink
New feature: Optional auto-close stream output on stream load
Browse files Browse the repository at this point in the history
Top Streams drop down populated with most popular games on screen load.
Fixed bug in twitch api client getting list of top games, wrong object was being used
Assembly version bumped to 2.5.0 for new feature
  • Loading branch information
laurencee committed Jun 30, 2016
1 parent c86cdbc commit bd3beea
Show file tree
Hide file tree
Showing 14 changed files with 137 additions and 35 deletions.
6 changes: 6 additions & 0 deletions ExternalAPIs.Tests/TwitchTvClientShould.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ public async Task GetTopGamesList()
var topGames = await sut.GetTopGames();
Assert.NotNull(topGames);
Assert.NotEmpty(topGames);

topGames.ForEach(x =>
{
Assert.NotNull(x.Game);
Assert.NotNull(x.Game.Name);
});
}

[Fact, Trait("Category", "LocalOnly")]
Expand Down
3 changes: 2 additions & 1 deletion ExternalAPIs/TwitchTv/Dto/QueryRoot/TopGamesRoot.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public class TopGamesRoot
[JsonProperty(PropertyName = "_total", NullValueHandling = NullValueHandling.Ignore)]
public int Total { get; set; }

public List<Game> Top { get; set; }
[JsonProperty(PropertyName = "top")]
public List<TopGame> TopGames { get; set; }
}
}
2 changes: 1 addition & 1 deletion ExternalAPIs/TwitchTv/ITwitchTvReadonlyClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public interface ITwitchTvReadonlyClient

Task<List<Stream>> GetStreamsDetails(IEnumerable<string> streamNames, CancellationToken cancellationToken = default(CancellationToken));

Task<List<Game>> GetTopGames(CancellationToken cancellationToken = default(CancellationToken));
Task<List<TopGame>> GetTopGames(CancellationToken cancellationToken = default(CancellationToken));

Task<List<Stream>> SearchStreams(string streamName, CancellationToken cancellationToken = default(CancellationToken));

Expand Down
4 changes: 2 additions & 2 deletions ExternalAPIs/TwitchTv/TwitchTvReadonlyClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ public class TwitchTvReadonlyClient : ITwitchTvReadonlyClient
return streamRoot.Streams;
}

public async Task<List<Game>> GetTopGames(CancellationToken cancellationToken = default(CancellationToken))
public async Task<List<TopGame>> GetTopGames(CancellationToken cancellationToken = default(CancellationToken))
{
var request = RequestConstants.TopGames;
var gamesRoot = await ExecuteRequest<TopGamesRoot>(request, cancellationToken);
return gamesRoot.Top;
return gamesRoot.TopGames;
}

public async Task<List<Stream>> SearchStreams(string streamName, CancellationToken cancellationToken = default(CancellationToken))
Expand Down
4 changes: 2 additions & 2 deletions GlobalAssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]

[assembly: AssemblyVersion("2.4.4.0")]
[assembly: AssemblyFileVersion("2.4.4.0")]
[assembly: AssemblyVersion("2.5.0.0")]
[assembly: AssemblyFileVersion("2.5.0.0")]
15 changes: 14 additions & 1 deletion Livestream.Monitor/Core/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public class Settings : PropertyChangedBase
private string chromeFullPath;
private int minimumEventViewers = DEFAULT_MINIMUM_EVENT_VIEWERS;
private bool disableNotifications;
private bool hideStreamOutputMessageBoxOnLoad;

[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public MetroThemeBaseColour? MetroThemeBaseColour
Expand Down Expand Up @@ -110,7 +111,19 @@ public bool DisableNotifications
NotifyOfPropertyChange(() => DisableNotifications);
}
}


[JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
public bool HideStreamOutputMessageBoxOnLoad
{
get { return hideStreamOutputMessageBoxOnLoad; }
set
{
if (value == hideStreamOutputMessageBoxOnLoad) return;
hideStreamOutputMessageBoxOnLoad = value;
NotifyOfPropertyChange(() => HideStreamOutputMessageBoxOnLoad);
}
}

/// <summary>
/// Channel names in this collection should not raise notifications. <para/>
/// We store these in settings so it can apply to both monitored and popular streams.
Expand Down
24 changes: 19 additions & 5 deletions Livestream.Monitor/Model/ApiClients/TwitchApiClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,8 +123,8 @@ public async Task<List<LivestreamQueryResult>> QueryChannels(CancellationToken c

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

int retryCount = 0;
while (moniteredChannels.Count > 0 && onlineStreams.Count == 0 && retryCount < 3)
{
Expand All @@ -139,7 +139,7 @@ public async Task<List<LivestreamQueryResult>> QueryChannels(CancellationToken c

retryCount++;
}

foreach (var onlineStream in onlineStreams)
{
if (cancellationToken.IsCancellationRequested) return queryResults;
Expand Down Expand Up @@ -167,7 +167,7 @@ public async Task<List<LivestreamQueryResult>> QueryChannels(CancellationToken c
queryOfflineStreams = false;
}
}

foreach (var offlineQueryResult in offlineQueryResultsCache.Except(queryResults).Where(x => x.IsSuccess))
{
offlineQueryResult.LivestreamModel.Offline();
Expand Down Expand Up @@ -226,6 +226,20 @@ public async Task<List<LivestreamQueryResult>> GetTopStreams(TopStreamQuery topS

public async Task<List<KnownGame>> GetKnownGameNames(string filterGameName)
{
if (string.IsNullOrEmpty(filterGameName))
{
return (await twitchTvClient.GetTopGames()).Select(x => new KnownGame()
{
GameName = x.Game.Name,
ThumbnailUrls = new ThumbnailUrls()
{
Medium = x.Game.Logo?.Medium,
Small = x.Game.Logo?.Small,
Large = x.Game.Logo?.Large
}
}).ToList();
}

var twitchGames = await twitchTvClient.SearchGames(filterGameName);
return twitchGames.Select(x => new KnownGame()
{
Expand Down Expand Up @@ -259,7 +273,7 @@ public async Task<List<LivestreamQueryResult>> GetUserFollows(string userName)
}

private async Task<List<LivestreamQueryResult>> GetOfflineStreamQueryResults(
IEnumerable<ChannelIdentifier> offlineChannels,
IEnumerable<ChannelIdentifier> offlineChannels,
CancellationToken cancellationToken)
{
return await offlineChannels.ExecuteInParallel(async channelIdentifier =>
Expand Down
38 changes: 20 additions & 18 deletions Livestream.Monitor/Model/StreamLauncher.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,16 +152,6 @@ private void StartLivestreamer(string livestreamerArgs, MessageBoxViewModel mess
// the process needs to be launched from its own thread so it doesn't lockup the UI
Task.Run(() =>
{
// work around an issue where focus isn't returned to the main window on the messagebox window closing (even though the owner is set correctly)
messageBoxViewModel.Deactivated += (sender, args) =>
{
var mainWindow = Application.Current.MainWindow;
if (mainWindow != null && mainWindow.IsVisible)
{
mainWindow.Activate();
}
};

var proc = new Process
{
StartInfo =
Expand All @@ -182,16 +172,22 @@ private void StartLivestreamer(string livestreamerArgs, MessageBoxViewModel mess
proc.ErrorDataReceived +=
(sender, args) =>
{
if (args.Data != null)
{
preventClose = true;
messageBoxViewModel.MessageText += Environment.NewLine + args.Data;
}
if (args.Data == null) return;

preventClose = true;
messageBoxViewModel.MessageText += Environment.NewLine + args.Data;
};
proc.OutputDataReceived +=
(sender, args) =>
{
if (args.Data != null) messageBoxViewModel.MessageText += Environment.NewLine + args.Data;
if (args.Data == null) return;
if (args.Data.StartsWith("[cli][info] Starting player") && settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad)
{
messageBoxViewModel.TryClose();
// can continue adding messages, the view model still exists so it doesn't really matter
}

messageBoxViewModel.MessageText += Environment.NewLine + args.Data;
};

try
Expand All @@ -202,7 +198,12 @@ private void StartLivestreamer(string livestreamerArgs, MessageBoxViewModel mess
proc.BeginOutputReadLine();

proc.WaitForExit();
if (proc.ExitCode != 0) preventClose = true;
if (proc.ExitCode != 0)
{
preventClose = true;
// open the message box if it was somehow closed prior to the error being displayed
if (!messageBoxViewModel.IsActive) windowManager.ShowWindow(messageBoxViewModel, null, new WindowSettingsBuilder().SizeToContent().NoResizeBorderless().Create());
}

onClose?.Invoke();
}
Expand All @@ -226,8 +227,9 @@ private MessageBoxViewModel ShowLivestreamerLoadMessageBox(string title, string
var messageBoxViewModel = new MessageBoxViewModel
{
DisplayName = title,
MessageText = messageText
MessageText = messageText,
};
messageBoxViewModel.ShowHideOnLoadCheckbox(settingsHandler);

var settings = new WindowSettingsBuilder().SizeToContent()
.NoResizeBorderless()
Expand Down
37 changes: 35 additions & 2 deletions Livestream.Monitor/ViewModels/MessageBoxViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
using Caliburn.Micro;
using System;
using Caliburn.Micro;
using Livestream.Monitor.Core;

namespace Livestream.Monitor.ViewModels
{
public class MessageBoxViewModel : Screen
{
private string messageText;
private bool hideOnLoadCheckboxVisible;
private bool hideOnLoad;
private ISettingsHandler settingsHandler;

public MessageBoxViewModel()
{
DisplayName = "Livestream Monitor";
}


public bool HideOnLoadCheckboxVisibleVisible => hideOnLoadCheckboxVisible;

public string MessageText
{
get { return messageText; }
Expand All @@ -21,5 +28,31 @@ public string MessageText
NotifyOfPropertyChange(() => MessageText);
}
}

public bool HideOnLoad
{
get { return hideOnLoad; }
set
{
if (value == hideOnLoad) return;
hideOnLoad = value;
if (settingsHandler != null)
{
settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad = hideOnLoad;
settingsHandler.SaveSettings();
}
NotifyOfPropertyChange(() => HideOnLoad);
}
}

/// <summary> Shows the hide on load checkbox requiring a settings handler to bind the checkbox to </summary>
public void ShowHideOnLoadCheckbox(ISettingsHandler settingsHandler)
{
if (settingsHandler == null) throw new ArgumentNullException(nameof(settingsHandler));

this.settingsHandler = settingsHandler;
hideOnLoadCheckboxVisible = true;
hideOnLoad = settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad;
}
}
}
18 changes: 17 additions & 1 deletion Livestream.Monitor/ViewModels/SettingsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class SettingsViewModel : Screen, INotifyDataErrorInfo
private string livestreamerFullPath;
private int minimumEventViewers;
private bool disableNotifications;
private bool hideStreamOutputOnLoad;

public SettingsViewModel()
{
Expand Down Expand Up @@ -109,6 +110,18 @@ public bool DisableNotifications
}
}

public bool HideStreamOutputOnLoad
{
get { return hideStreamOutputOnLoad; }
set
{
if (value == hideStreamOutputOnLoad) return;
hideStreamOutputOnLoad = value;
NotifyOfPropertyChange(() => HideStreamOutputOnLoad);
NotifyOfPropertyChange(() => CanSave);
}
}

public bool CanSave
{
get
Expand All @@ -121,7 +134,8 @@ public bool CanSave
return ChromeFullPath != settingsHandler.Settings.ChromeFullPath ||
LivestreamerFullPath != settingsHandler.Settings.LivestreamerFullPath ||
MinimumEventViewers != settingsHandler.Settings.MinimumEventViewers ||
DisableNotifications != settingsHandler.Settings.DisableNotifications;
DisableNotifications != settingsHandler.Settings.DisableNotifications ||
HideStreamOutputOnLoad != settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad;
}
}

Expand All @@ -148,6 +162,7 @@ public void Save()
settingsHandler.Settings.LivestreamerFullPath = LivestreamerFullPath;
settingsHandler.Settings.MinimumEventViewers = MinimumEventViewers;
settingsHandler.Settings.DisableNotifications = DisableNotifications;
settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad = HideStreamOutputOnLoad;
settingsHandler.SaveSettings();

settingsHandler.Settings.PropertyChanged += SettingsOnPropertyChanged;
Expand Down Expand Up @@ -207,6 +222,7 @@ protected override void OnActivate()
ChromeFullPath = settingsHandler.Settings.ChromeFullPath;
MinimumEventViewers = settingsHandler.Settings.MinimumEventViewers;
DisableNotifications = settingsHandler.Settings.DisableNotifications;
HideStreamOutputOnLoad = settingsHandler.Settings.HideStreamOutputMessageBoxOnLoad;

settingsHandler.Settings.PropertyChanged += SettingsOnPropertyChanged;
base.OnActivate();
Expand Down
8 changes: 8 additions & 0 deletions Livestream.Monitor/ViewModels/TopStreamsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,14 @@ protected override async void OnViewLoaded(object view)
if (Execute.InDesignMode) return;

await EnsureItems();
try
{
var games = await apiClientFactory.Get<TwitchApiClient>().GetKnownGameNames(null);
PossibleGameNames.Clear();
PossibleGameNames.AddRange(games.Select(x => x.GameName));
}
catch { }

base.OnViewLoaded(view);
}

Expand Down
9 changes: 8 additions & 1 deletion Livestream.Monitor/Views/MessageBoxView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,15 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ScrollViewer MaxWidth="700" MaxHeight="500" VerticalScrollBarVisibility="Auto" HorizontalScrollBarVisibility="Disabled">
<TextBox x:Name="MessageText" Margin="10,10,10,20" TextWrapping="Wrap" IsReadOnly="True" />
<TextBox x:Name="MessageText" Margin="10,10,10,10" TextWrapping="Wrap" IsReadOnly="True" />
</ScrollViewer>
<CheckBox Grid.Row="1" x:Name="HideOnLoad" Content="Hide on load" Visibility="{Binding ShowHideOnLoadCheckbox, Converter={StaticResource BooleanToVisibilityConverter}}"
ToolTip="Hides this message box automatically once a stream has loaded successfully"
Margin="10,0,10,10" />
</Grid>
</UserControl>
2 changes: 2 additions & 0 deletions Livestream.Monitor/Views/SettingsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
ToolTip="Minimum nuber of viewers for a stream to be shown in a notification as a popular/event stream.&#10;This helps you not miss out on special events/popular streams." />
<CheckBox x:Name="DisableNotifications" Content="Disable Notifications"
ToolTip="When unchecked, will disable all notifications (including online notifications) from displaying" />
<CheckBox x:Name="HideStreamOutputOnLoad" Content="Hide Stream Output On Load"
ToolTip="When checked, the stream output box will be hidden upon successful stream load" />
</StackPanel>

<Button Grid.Row="1" x:Name="Save" Content="Save" Margin="20" />
Expand Down
2 changes: 1 addition & 1 deletion Livestream.Monitor/Views/TopStreamsView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<ComboBox x:Name="ApiClients" Margin="3" DisplayMemberPath="ApiName" MinWidth="80" />
<TextBlock Text="Game Filter" />
<ComboBox x:Name="PossibleGameNames" Text="{Binding GameName, UpdateSourceTrigger=PropertyChanged, Delay=250}" IsDropDownOpen="{Binding ExpandPossibleGames}"
Width="175" Margin="3" ToolTip="Filter to show only streams from a particular game. This must be an EXACT match e.g. League of Legends rather than just league, so use the auto-completion drop down option."
Width="175" Margin="3" ToolTip="Filter to show only streams from a particular game. You can type a partial game name to search for any matching game names from twitch."
controls:TextBoxHelper.Watermark="Partial game name..."
IsEditable="True" IsTextSearchEnabled="True" IsTextSearchCaseSensitive="False" />
<Button x:Name="RefreshItems" Content="{StaticResource RefreshButtonText}" FontWeight="ExtraBold" Margin="2" ToolTip="Refresh Top Streams" />
Expand Down

0 comments on commit bd3beea

Please sign in to comment.