| 
					
				 | 
			
			
				@@ -9,6 +9,60 @@ using MediaBrowser.Model.Net; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public interface IHdHomerunChannelCommands 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        IEnumerable<Tuple<string, string>> GetCommands(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public class LegacyHdHomerunChannelCommands : IHdHomerunChannelCommands 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string _channel; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string _program; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public LegacyHdHomerunChannelCommands(string url) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            // parse url for channel and program 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var regExp = new Regex(@"\/ch(\d+)-?(\d*)"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var match = regExp.Match(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (match.Success) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                _channel = match.Groups[1].Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                _program = match.Groups[2].Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public IEnumerable<Tuple<string, string>> GetCommands() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var commands = new List<Tuple<string, string>>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!String.IsNullOrEmpty(_channel)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commands.Add(Tuple.Create("channel", _channel)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!String.IsNullOrEmpty(_program)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commands.Add(Tuple.Create("program", _program)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return commands; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    public class HdHomerunChannelCommands : IHdHomerunChannelCommands 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        private string _channel; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public HdHomerunChannelCommands(string channel) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            _channel = channel; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public IEnumerable<Tuple<string, string>> GetCommands() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            var commands = new List<Tuple<string, string>>(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!String.IsNullOrEmpty(_channel)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                commands.Add(Tuple.Create("vchannel", _channel)); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return commands; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     public class HdHomerunManager : IDisposable 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         public static int HdHomeRunPort = 65001; 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -57,16 +111,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        public async Task StartStreaming(IpAddressInfo remoteIp, IpAddressInfo localIp, int localPort, string url, int numTuners, CancellationToken cancellationToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        public async Task StartStreaming(IpAddressInfo remoteIp, IpAddressInfo localIp, int localPort, IHdHomerunChannelCommands commands, int numTuners, CancellationToken cancellationToken) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             _remoteIp = remoteIp; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // parse url for channel and program 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            string frequency, program; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (!ParseUrl(url, out frequency, out program)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+             
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             using (var tcpClient = _socketFactory.CreateTcpSocket(_remoteIp, HdHomeRunPort)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                 if (!_lockkey.HasValue) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -92,20 +140,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    var channelMsg = CreateSetMessage(i, "channel", frequency, _lockkey.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    await tcpClient.SendAsync(channelMsg, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // parse response to make sure it worked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        await ReleaseLockkey(tcpClient).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if (program != String.Empty) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    var commandList = commands.GetCommands(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    foreach(Tuple<string,string> command in commandList) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        var programMsg = CreateSetMessage(i, "program", program, _lockkey.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        await tcpClient.SendAsync(programMsg, programMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        var channelMsg = CreateSetMessage(i, command.Item1, command.Item2, _lockkey.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        await tcpClient.SendAsync(channelMsg, channelMsg.Length, ipEndPoint, cancellationToken).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         await tcpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         // parse response to make sure it worked 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         if (!ParseReturnMessage(response.Buffer, response.ReceivedBytes, out returnVal)) 
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -113,8 +152,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             await ReleaseLockkey(tcpClient).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                             continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                     
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     var targetValue = String.Format("rtp://{0}:{1}", localIp, localPort); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				                     var targetMsg = CreateSetMessage(i, "target", targetValue, _lockkey.Value); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
	
		
			
				| 
					
				 | 
			
			
				@@ -154,22 +194,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             await tcpClient.ReceiveAsync(CancellationToken.None).ConfigureAwait(false); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        private static bool ParseUrl(string url, out string frequency, out string program) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            frequency = String.Empty; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            program = String.Empty; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var regExp = new Regex(@"\/ch(\d+)-?(\d*)"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var match = regExp.Match(url); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if (match.Success) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                frequency = match.Groups[1].Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                program = match.Groups[2].Value; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return true; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return false; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         private static byte[] CreateGetMessage(int tuner, string name) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				             var byteName = Encoding.UTF8.GetBytes(String.Format("/tuner{0}/{1}\0", tuner, name)); 
			 |