Skip to content

Commit

Permalink
Add support for http video or audio casting to Chromecast or Google H…
Browse files Browse the repository at this point in the history
…ome.
  • Loading branch information
Viktar Karpach committed Mar 21, 2018
1 parent 41ff2a1 commit db37737
Show file tree
Hide file tree
Showing 11 changed files with 203 additions and 87 deletions.
16 changes: 14 additions & 2 deletions Karpach.Remote.Chromecast.Command.Runner/App.config
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Reactive.Core" publicKeyToken="94bc3704cddfc263" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.3000.0" newVersion="3.0.3000.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Reactive.Linq" publicKeyToken="94bc3704cddfc263" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.0.3000.0" newVersion="3.0.3000.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>
Original file line number Diff line number Diff line change
Expand Up @@ -33,40 +33,57 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="Google.Protobuf, Version=3.3.0.0, Culture=neutral, PublicKeyToken=a7d26565bac4d604, processorArchitecture=MSIL">
<HintPath>..\packages\Google.Protobuf.3.3.0\lib\net45\Google.Protobuf.dll</HintPath>
<Reference Include="GoogleCast, Version=1.3.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\GoogleCast.1.3.3\lib\netstandard2.0\GoogleCast.dll</HintPath>
</Reference>
<Reference Include="Karpach.Remote.Commands.Base, Version=1.0.9.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Karpach.Remote.Commands.Base.1.0.10\lib\net461\Karpach.Remote.Commands.Base.dll</HintPath>
</Reference>
<Reference Include="Karpach.Remote.Commands.Interfaces, Version=1.0.5.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Karpach.Remote.Commands.Interfaces.1.0.5\lib\net461\Karpach.Remote.Commands.Interfaces.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.9.0.1\lib\net45\Newtonsoft.Json.dll</HintPath>
<Reference Include="Microsoft.Extensions.DependencyInjection, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.dll</HintPath>
</Reference>
<Reference Include="Rssdp, Version=3.5.4.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Rssdp.3.5.4\lib\net45\Rssdp.dll</HintPath>
<Reference Include="Microsoft.Extensions.DependencyInjection.Abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.0.0\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll</HintPath>
</Reference>
<Reference Include="protobuf-net, Version=2.3.2.0, Culture=neutral, PublicKeyToken=257b51d87d2e4d67, processorArchitecture=MSIL">
<HintPath>..\packages\protobuf-net.2.3.2\lib\net40\protobuf-net.dll</HintPath>
</Reference>
<Reference Include="SharpConfig, Version=3.2.0.0, Culture=neutral, PublicKeyToken=c1deedac91bd7724, processorArchitecture=MSIL">
<HintPath>..\packages\sharpconfig.3.2.0\lib\net20\SharpConfig.dll</HintPath>
</Reference>
<Reference Include="Sockets.Plugin, Version=2.0.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharpcaster.SocketsForPCL.2.0.4\lib\net45\Sockets.Plugin.dll</HintPath>
</Reference>
<Reference Include="Sockets.Plugin.Abstractions, Version=2.0.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Sharpcaster.SocketsForPCL.2.0.4\lib\net45\Sockets.Plugin.Abstractions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Reactive.Core, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.Core.3.1.1\lib\net46\System.Reactive.Core.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Interfaces, Version=3.0.1000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.Interfaces.3.1.1\lib\net45\System.Reactive.Interfaces.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Linq, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.Linq.3.1.1\lib\net46\System.Reactive.Linq.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.PlatformServices, Version=3.0.3000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.PlatformServices.3.1.1\lib\net46\System.Reactive.PlatformServices.dll</HintPath>
</Reference>
<Reference Include="System.Reactive.Windows.Threading, Version=3.0.1000.0, Culture=neutral, PublicKeyToken=94bc3704cddfc263, processorArchitecture=MSIL">
<HintPath>..\packages\System.Reactive.Windows.Threading.3.1.1\lib\net45\System.Reactive.Windows.Threading.dll</HintPath>
</Reference>
<Reference Include="System.Windows" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
<Reference Include="Zeroconf, Version=2.9.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Zeroconf.2.9.0\lib\net45\Zeroconf.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Program.cs" />
Expand Down
5 changes: 3 additions & 2 deletions Karpach.Remote.Chromecast.Command.Runner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ static void Main(string[] args)
Id = 41d5f7ea-6e02-4c91-8c25-5100e1240952
");
var command = new ChromecastCommand();
//command.ShowSettings();
command.RunCommand("hDz2F5EDZZI");
command.ShowSettings();
//command.RunCommand("hDz2F5EDZZI");
command.RunCommand("http://ic2.101.ru:8000/v3_1", "audio/mp3");
Console.ReadKey();
command.Dispose();
}
Expand Down
15 changes: 11 additions & 4 deletions Karpach.Remote.Chromecast.Command.Runner/packages.config
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Google.Protobuf" version="3.3.0" targetFramework="net46" />
<package id="GoogleCast" version="1.3.3" targetFramework="net461" />
<package id="Karpach.Remote.Commands.Base" version="1.0.10" targetFramework="net461" />
<package id="Karpach.Remote.Commands.Interfaces" version="1.0.5" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net46" />
<package id="Rssdp" version="3.5.4" targetFramework="net46" />
<package id="Sharpcaster.SocketsForPCL" version="2.0.4" targetFramework="net46" />
<package id="Microsoft.Extensions.DependencyInjection" version="2.0.0" targetFramework="net461" />
<package id="Microsoft.Extensions.DependencyInjection.Abstractions" version="2.0.0" targetFramework="net461" />
<package id="protobuf-net" version="2.3.2" targetFramework="net461" />
<package id="sharpconfig" version="3.2.0" targetFramework="net46" />
<package id="System.Reactive" version="3.1.1" targetFramework="net461" />
<package id="System.Reactive.Core" version="3.1.1" targetFramework="net461" />
<package id="System.Reactive.Interfaces" version="3.1.1" targetFramework="net461" />
<package id="System.Reactive.Linq" version="3.1.1" targetFramework="net461" />
<package id="System.Reactive.PlatformServices" version="3.1.1" targetFramework="net461" />
<package id="System.Reactive.Windows.Threading" version="3.1.1" targetFramework="net461" />
<package id="Zeroconf" version="2.9.0" targetFramework="net461" />
</packages>
91 changes: 63 additions & 28 deletions Karpach.Remote.Chromecast.Command/ChromecastCommand.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
using System;
using System.Collections.ObjectModel;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using GoogleCast;
using GoogleCast.Channels;
using GoogleCast.Models.Media;
using Karpach.Remote.Commands.Base;
using Karpach.Remote.Commands.Interfaces;
using NLog;
using SharpCaster.Services;
using Image = System.Drawing.Image;

namespace Karpach.Remote.Chromecast.Command
{
Expand Down Expand Up @@ -42,47 +44,80 @@ public override void RunCommand(params object[] parameters)
{
Thread.Sleep(delay.Value);
}
if (parameters != null && parameters.Length == 1)
if (parameters != null && parameters.Length >= 1)
{
string parameter = parameters[0].ToString();
PlayYoutubeVideoAsync(parameter).Wait();
string videoId = parameters[0].ToString();
string contentType = parameters.Length > 1 ? parameters[1].ToString() : null;
PlayVideoAsync(videoId, contentType).Wait();
}
else
{
PlayYoutubeVideoAsync("KpllAjxOIUU").Wait();
PlayVideoAsync("KpllAjxOIUU").Wait();
}
}

private Task PlayYoutubeVideoAsync(string videoId)
private Task PlayVideoAsync(string videoId, string contentType = null)
{
return Task.Run(async () =>
{
string ip = ((ChromecastCommandSettings) Settings).ChromeCastIP;
if (string.IsNullOrEmpty(ip))
ChromecastCommandSettings settings = (ChromecastCommandSettings)Settings;
IReceiver[] receivers = (await new DeviceLocator().FindReceiversAsync().ConfigureAwait(false)).ToArray();
if (!receivers.Any())
{
ObservableCollection<SharpCaster.Models.Chromecast> chromecasts = await ChromecastService.Current
.StartLocatingDevices()
.ConfigureAwait(false);
if (!chromecasts.Any())
{
Logger.Log(LogLevel.Error, "No ChromeCasts found, try to specify ip of chromecast manually.");
return;
}
var chromecast = chromecasts.First();
ip = chromecast.DeviceUri.Host;
}
string url = $"http://{ip}:8008/apps/YouTube";
HttpClient client = new HttpClient();
StringContent httpContent = new StringContent($"v={videoId}", Encoding.UTF8, "application/json");
HttpResponseMessage httpResponseMessage = await client.PostAsync(url, httpContent).ConfigureAwait(false);
HttpResponseMessage response = httpResponseMessage;
if (response.StatusCode != HttpStatusCode.Created)
Logger.Log(LogLevel.Error, "No ChromeCasts found.");
return;
}
IReceiver chromeCast;
if (!string.IsNullOrEmpty(settings.ChromeCastName))
{
Logger.Log(LogLevel.Error, response.Content);
chromeCast = receivers.FirstOrDefault(r => string.Equals(settings.ChromeCastName, r.FriendlyName, StringComparison.InvariantCultureIgnoreCase)) ??
receivers.First();
}
else
{
chromeCast = receivers.First();
}
if (Regex.IsMatch(videoId, "^https?://"))
{
await PlayVideoAsync(chromeCast, videoId, contentType).ConfigureAwait(false);
}
else
{
await PlayYoutubeAsync(chromeCast.IPEndPoint.Address.ToString(), videoId).ConfigureAwait(false);
}
});
}

private async Task PlayYoutubeAsync(string ip, string videoId)
{
string url = $"http://{ip}:8008/apps/YouTube";
HttpClient client = new HttpClient();
StringContent httpContent = new StringContent($"v={videoId}", Encoding.UTF8, "application/json");
HttpResponseMessage httpResponseMessage = await client.PostAsync(url, httpContent).ConfigureAwait(false);
HttpResponseMessage response = httpResponseMessage;
if (response.StatusCode != HttpStatusCode.Created)
{
Logger.Log(LogLevel.Error, response.Content);
}
}

private async Task PlayVideoAsync(IReceiver chromeCast, string url, string contentType)
{
var sender = new Sender();
// Connect to the ChromeCast
await sender.ConnectAsync(chromeCast).ConfigureAwait(false);
// Launch the default media receiver application
var mediaChannel = sender.GetChannel<IMediaChannel>();
await sender.LaunchAsync(mediaChannel).ConfigureAwait(false);
// Load and play video or audio over http
await mediaChannel.LoadAsync(new Media
{
ContentId = url,
ContentType = contentType
}).ConfigureAwait(false);
}

public override void ShowSettings()
{
var dlg = new ChromecastCommandSettingsForm((ChromecastCommandSettings)Settings);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@ public class ChromecastCommandSettings : CommandSettingsBase
{
public string CommandName { get; set; }
public int? ExecutionDelay { get; set; }
public string ChromeCastIP { get; set; }
public string ChromeCastName { get; set; }
}
}
42 changes: 25 additions & 17 deletions Karpach.Remote.Chromecast.Command/ChromecastCommandSettingsForm.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using GoogleCast;
using SampleCommand;

namespace Karpach.Remote.Chromecast.Command
Expand All @@ -14,7 +19,7 @@ public class ChromecastCommandSettingsForm : Form
private TextBox _txtCommandName;
private Label _lbDelay;
private Label lbIP;
private TextBox _txtChromeCastIP;
private ComboBox _cbxChromeCast;
private TextBox _txtDelay;

public ChromecastCommandSettingsForm(ChromecastCommandSettings settings)
Expand All @@ -23,7 +28,10 @@ public ChromecastCommandSettingsForm(ChromecastCommandSettings settings)
Settings = settings;
_txtCommandName.Text = Settings.CommandName;
_txtDelay.Text = Settings.ExecutionDelay?.ToString() ?? "0";
_txtChromeCastIP.Text = Settings.ChromeCastIP;
Task.Factory.StartNew(async () => {
string[] receivers = (await new DeviceLocator().FindReceiversAsync()).Select(r => r.FriendlyName).ToArray();
_cbxChromeCast.DataSource = receivers;
},CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());
}

private void InitializeComponent()
Expand All @@ -36,7 +44,7 @@ private void InitializeComponent()
this._lbDelay = new System.Windows.Forms.Label();
this._txtDelay = new System.Windows.Forms.TextBox();
this.lbIP = new System.Windows.Forms.Label();
this._txtChromeCastIP = new System.Windows.Forms.TextBox();
this._cbxChromeCast = new System.Windows.Forms.ComboBox();
this.SuspendLayout();
//
// _btnOk
Expand Down Expand Up @@ -96,25 +104,26 @@ private void InitializeComponent()
// lbIP
//
this.lbIP.AutoSize = true;
this.lbIP.Location = new System.Drawing.Point(9, 94);
this.lbIP.Location = new System.Drawing.Point(57, 94);
this.lbIP.Name = "lbIP";
this.lbIP.Size = new System.Drawing.Size(122, 13);
this.lbIP.Size = new System.Drawing.Size(67, 13);
this.lbIP.TabIndex = 0;
this.lbIP.Text = "Optional ChromeCast IP:";
this.lbIP.Text = "ChromeCast:";
//
// txtChromeCastIP
// cbxChromeCast
//
this._txtChromeCastIP.Location = new System.Drawing.Point(130, 91);
this._txtChromeCastIP.Name = "_txtChromeCastIP";
this._txtChromeCastIP.Size = new System.Drawing.Size(257, 20);
this._txtChromeCastIP.TabIndex = 1;
this._cbxChromeCast.FormattingEnabled = true;
this._cbxChromeCast.Location = new System.Drawing.Point(130, 89);
this._cbxChromeCast.Name = "_cbxChromeCast";
this._cbxChromeCast.Size = new System.Drawing.Size(255, 21);
this._cbxChromeCast.TabIndex = 4;
//
// ChromecastCommandSettingsForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(397, 168);
this.Controls.Add(this._txtChromeCastIP);
this.Controls.Add(this._cbxChromeCast);
this.Controls.Add(this._txtDelay);
this.Controls.Add(this.lbIP);
this.Controls.Add(this._txtCommandName);
Expand All @@ -126,7 +135,7 @@ private void InitializeComponent()
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "ChromecastCommandSettingsForm";
this.Text = "ChromeCast Command Settings";
this.Text = "ChromeCast Command Settings";
this.ResumeLayout(false);
this.PerformLayout();

Expand All @@ -135,15 +144,14 @@ private void InitializeComponent()
private void _btnOk_Click(object sender, EventArgs e)
{
Settings.CommandName = _txtCommandName.Text;
int n;
Settings.ExecutionDelay = int.TryParse(_txtDelay.Text, out n) ? n : 0;
Settings.ChromeCastIP = _txtChromeCastIP.Text;
Settings.ExecutionDelay = int.TryParse(_txtDelay.Text, out var n) ? n : 0;
Settings.ChromeCastName = _cbxChromeCast.Text;
Close();
}

private void _btnCancel_Click(object sender, EventArgs e)
{
Close();
}
}
}
}

0 comments on commit db37737

Please sign in to comment.