Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions GoogleCast.ConsoleApp/GoogleCast.ConsoleApp.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="CommandLineParser" Version="2.9.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\GoogleCast\GoogleCast.csproj" />
</ItemGroup>

</Project>
71 changes: 71 additions & 0 deletions GoogleCast.ConsoleApp/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@

using System.Net;
using CommandLine;
using GoogleCast;
using GoogleCast.Channels;
using GoogleCast.Models.Cast;


class ConsoleApp
{
public class Options
{
public Options() { Host = string.Empty; }

[Option('h', "host", Required = true, HelpText = "Host")]
public string Host { get; set; }
[Option('p', "port", Required = false, Default = 8009, HelpText = "Port")]
public int Port { get; set; }
[Option('t', "timeout", Required = false, Default = 5000, HelpText = "Timeout")]
public int Timeout { get; set; }
}

static void Main(string[] args)
{
Parser.Default.ParseArguments<Options>(args)
.WithParsed<Options>(o =>
{
Task.Run(async () =>
{
var sender = new Sender();
Console.Write("Connection...");

var connected = await sender.ConnectAsync(IPAddress.Parse(o.Host), o.Port, o.Timeout);
if (!connected)
{
Console.WriteLine("Unable to connect to " + o.Host);
return;
}

Console.WriteLine("Done.");

var alive = false;
var heartBeatChannel = sender.GetChannel<IHeartbeatChannel>();
heartBeatChannel.PingReceived += (object? sender, GoogleCast.Models.HeartBeat.PingEvent e) =>
{
alive = true;
};

Console.Write("Waiting for ChromeCast Feedback");
while (!alive)
{
await Task.Delay(1000);
Console.Write(".");

}
Console.WriteLine(" Done.");

// Launch the default media receiver application
Console.Write("Initializing Cast Channel...");
var castChannel = sender.GetChannel<ICastChannel>();
await sender.LaunchAsync(castChannel);
Console.WriteLine(" Done.");

// Load an example website
Console.Write("Load URL http://www.example.com...");
await castChannel.LoadUrl(new CastInformation { Url = "https://www.example.com" });
Console.WriteLine(" Done.");
}).GetAwaiter().GetResult();
});
}
}
102 changes: 101 additions & 1 deletion GoogleCast.SampleApp/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using GoogleCast.Channels;
using GoogleCast.Models.Media;
Expand Down Expand Up @@ -31,6 +32,8 @@ public MainViewModel(IDeviceLocator deviceLocator, ISender sender)
PauseCommand = new RelayCommand(async () => await TryAsync(PauseAsync), () => AreButtonsEnabled);
StopCommand = new RelayCommand(async () => await TryAsync(StopAsync), () => AreButtonsEnabled);
RefreshCommand = new RelayCommand(async () => await TryAsync(RefreshAsync), () => IsLoaded);
ConnectCommand = new RelayCommand(async () => await TryAsync(ConnectAsync), () => IsLoaded);
CastCommand = new RelayCommand(async () => await TryAsync(CastAsync), () => IsCastButtonEnabled);
}

private IDeviceLocator DeviceLocator { get; }
Expand Down Expand Up @@ -68,6 +71,28 @@ public IReceiver? SelectedReceiver
}
}

private string? _editedReceiver;
/// <summary>
/// Gets or sets the selected receiver
/// </summary>
public string? EditedReceiver
{
get => _editedReceiver;
set
{
if (_editedReceiver != null && !_editedReceiver.Equals(value) ||
_editedReceiver == null && value != null)
{
_editedReceiver = value;
IsInitialized = false;
OnPropertyChanged(nameof(EditedReceiver));
NotifyButtonsCommandsCanExecuteChanged();
}
}
}



private bool _isLoaded;
/// <summary>
/// Gets a value indicating whether the list of the GoogleCast devices is loaded or not
Expand All @@ -82,6 +107,8 @@ private set
_isLoaded = value;
OnPropertyChanged(nameof(IsLoaded));
RefreshCommand.NotifyCanExecuteChanged();
ConnectCommand.NotifyCanExecuteChanged();
CastCommand.NotifyCanExecuteChanged();
NotifyButtonsCommandsCanExecuteChanged();
}
}
Expand All @@ -90,7 +117,22 @@ private set
/// <summary>
/// Gets a value indicating whether the Play, Pause and Stop buttons must be enabled or not
/// </summary>
public bool AreButtonsEnabled => IsLoaded && SelectedReceiver != null && !string.IsNullOrWhiteSpace(Link);
public bool AreButtonsEnabled => IsLoaded &&
(
(SelectedReceiver != null && !string.IsNullOrWhiteSpace(Link))
||
(EditedReceiver != null && !string.IsNullOrWhiteSpace(Link))
);

/// <summary>
/// Gets a value indicating whether the Cast buttons must be enabled or not
/// </summary>
public bool IsCastButtonEnabled => IsLoaded &&
(
(SelectedReceiver != null && !string.IsNullOrWhiteSpace(CastUrl))
||
(EditedReceiver != null && !string.IsNullOrWhiteSpace(CastUrl))
);

private string? _playerState;
/// <summary>
Expand Down Expand Up @@ -121,6 +163,25 @@ public string Link
}
}

private string _castUrl = "https://example.com";
/// <summary>
/// Gets or sets the url to cast
/// </summary>
public string CastUrl
{
get => _castUrl;
set
{
if (_castUrl != value)
{
_link = value;
IsInitialized = false;
OnPropertyChanged(nameof(CastUrl));
NotifyButtonsCommandsCanExecuteChanged();
}
}
}

private string _subtitle = "https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/tracks/DesigningForGoogleCast-en.vtt";
/// <summary>
/// Gets or sets the subtitle file
Expand Down Expand Up @@ -196,13 +257,22 @@ private bool IsStopped
/// Gets the refresh command
/// </summary>
public RelayCommand RefreshCommand { get; }
/// <summary>
/// Gets the connect command
/// </summary>
public RelayCommand ConnectCommand { get; }
/// <summary>
/// Gets the connect command
/// </summary>
public RelayCommand CastCommand { get; }

private void NotifyButtonsCommandsCanExecuteChanged()
{
OnPropertyChanged(nameof(AreButtonsEnabled));
PlayCommand.NotifyCanExecuteChanged();
PauseCommand.NotifyCanExecuteChanged();
StopCommand.NotifyCanExecuteChanged();
CastCommand.NotifyCanExecuteChanged();
}

private async Task TryAsync(Func<Task> action)
Expand Down Expand Up @@ -240,9 +310,39 @@ private async Task<bool> ConnectAsync()
await Sender.ConnectAsync(selectedReceiver);
return true;
}
else
{
if(!string.IsNullOrEmpty(EditedReceiver))
{
await Sender.ConnectAsync(
new Receiver
{
IPEndPoint = new System.Net.IPEndPoint(
IPAddress.Parse(EditedReceiver),
8009
)
});
return true;
}
}
return false;
}

private async Task<bool> CastAsync()
{
await SendChannelCommandAsync<ICastChannel>(true,
async channel =>
{
var sender = Sender;
var castChannel = sender.GetChannel<ICastChannel>();
await sender.LaunchAsync(castChannel);
await channel.LoadUrl(new Models.Cast.CastInformation { Url = CastUrl });
},
c=> Task.CompletedTask);

return true;
}

private async Task PlayAsync()
{
await SendChannelCommandAsync<IMediaChannel>(!IsInitialized || IsStopped,
Expand Down
26 changes: 19 additions & 7 deletions GoogleCast.SampleApp/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,29 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Title="GoogleCast" Height="200" Width="784"
Title="GoogleCast" Height="229" Width="784"
ResizeMode="CanResizeWithGrip"
Loaded="WindowLoadedAsync"
DataContext="{Binding Main, Source={StaticResource ViewModelLocator}}">
DataContext="{Binding Main, Source={StaticResource ViewModelLocator}}" WindowStartupLocation="CenterScreen">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="589*" />
<ColumnDefinition Width="123*"/>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<ComboBox ItemsSource="{Binding Receivers}" DisplayMemberPath="FriendlyName" SelectedValue="{Binding SelectedReceiver, Mode=TwoWay}" IsEnabled="{Binding IsLoaded}" Margin="4" />
<Button Grid.Column="1" Margin="4" Padding="4" Width="64" Command="{Binding RefreshCommand}">Refresh</Button>
<ComboBox ItemsSource="{Binding Receivers}" DisplayMemberPath="FriendlyName" Text="{Binding EditedReceiver, Mode=TwoWay}" SelectedValue="{Binding SelectedReceiver, Mode=TwoWay}" IsEnabled="{Binding IsLoaded}" Margin="4,4,4,4" Grid.ColumnSpan="2" IsEditable="True" />
<Button Grid.Column="2" Margin="4,4,4,4" Padding="4" Width="64" Command="{Binding RefreshCommand}">Refresh</Button>
<Button Grid.Column="3" Margin="4,4,4,4" Padding="4" Width="64" Command="{Binding ConnectCommand}">Connect</Button>
</Grid>
<Grid Grid.Row="1">
<Grid.ColumnDefinitions>
Expand All @@ -37,14 +41,22 @@
<Button Grid.Column="3" Margin="4" Padding="4" Width="64" Command="{Binding StopCommand}">Stop</Button>
</Grid>
<Grid Grid.Row="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Margin="4" Text="{Binding CastUrl, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" />
<Button Grid.Column="3" Margin="4" Padding="4" Width="64" Command="{Binding CastCommand}">Cast</Button>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Label VerticalAlignment="Center">Subtitle file</Label>
<TextBox Grid.Column="1" Margin="4" Text="{Binding Subtitle, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center" />
</Grid>
<Grid Grid.Row="3">
<Grid Grid.Row="4">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
Expand All @@ -56,6 +68,6 @@
<CheckBox Grid.Column="2" Margin="4" IsChecked="{Binding IsMuted, Mode=TwoWay}" VerticalAlignment="Center"
IsEnabled="{Binding AreButtonsEnabled}">Mute</CheckBox>
</Grid>
<TextBlock Grid.Row="4" TextAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding PlayerState}" />
<TextBlock Grid.Row="5" TextAlignment="Center" VerticalAlignment="Center" TextWrapping="Wrap" Text="{Binding PlayerState}" />
</Grid>
</Window>
10 changes: 8 additions & 2 deletions GoogleCast.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31205.134
# Visual Studio Version 17
VisualStudioVersion = 17.3.32811.315
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GoogleCast", "GoogleCast\GoogleCast.csproj", "{0B336E66-C5C4-47B7-BD45-85847CCEF991}"
EndProject
Expand All @@ -13,6 +13,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
.github\workflows\dotnet.yml = .github\workflows\dotnet.yml
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GoogleCast.ConsoleApp", "GoogleCast.ConsoleApp\GoogleCast.ConsoleApp.csproj", "{CA4470AE-1BD2-4BA3-86A8-45235252E0D1}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -27,6 +29,10 @@ Global
{347B2A0B-04B1-40D7-A63E-D0077A651306}.Debug|Any CPU.Build.0 = Debug|Any CPU
{347B2A0B-04B1-40D7-A63E-D0077A651306}.Release|Any CPU.ActiveCfg = Release|Any CPU
{347B2A0B-04B1-40D7-A63E-D0077A651306}.Release|Any CPU.Build.0 = Release|Any CPU
{CA4470AE-1BD2-4BA3-86A8-45235252E0D1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA4470AE-1BD2-4BA3-86A8-45235252E0D1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA4470AE-1BD2-4BA3-86A8-45235252E0D1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CA4470AE-1BD2-4BA3-86A8-45235252E0D1}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading