From 8a68c2383866c7e0b21dc164f1126bd78699c1fb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 15:54:50 -0400 Subject: [PATCH 1/6] update socket send functions --- Emby.Common.Implementations/Net/UdpSocket.cs | 83 +++++++++++++++---- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 8 +- 2 files changed, 75 insertions(+), 16 deletions(-) diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 94d073bd2..834f0a05c 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -145,31 +145,84 @@ namespace Emby.Common.Implementations.Net if (buffer == null) throw new ArgumentNullException("messageData"); if (endPoint == null) throw new ArgumentNullException("endPoint"); - cancellationToken.ThrowIfCancellationRequested(); + var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); - var tcs = new TaskCompletionSource(); +#if NETSTANDARD1_6 - cancellationToken.Register(() => tcs.TrySetCanceled()); - - _sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); - _sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); - _currentSendTaskCompletionSource = tcs; - - var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); - - if (!willRaiseEvent) + if (size != buffer.Length) { - _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); + byte[] copy = new byte[size]; + Buffer.BlockCopy(buffer, 0, copy, 0, size); + buffer = copy; } - return tcs.Task; + cancellationToken.ThrowIfCancellationRequested(); + + _Socket.SendTo(buffer, ipEndPoint); + return Task.FromResult(true); +#else + var taskSource = new TaskCompletionSource(); + + try + { + _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result => + { + if (cancellationToken.IsCancellationRequested) + { + taskSource.TrySetCanceled(); + return; + } + try + { + _Socket.EndSend(result); + taskSource.TrySetResult(true); + } + catch (Exception ex) + { + taskSource.TrySetException(ex); + } + + }, null); + } + catch (Exception ex) + { + taskSource.TrySetException(ex); + } + + //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); + + return taskSource.Task; +#endif + //ThrowIfDisposed(); + + //if (buffer == null) throw new ArgumentNullException("messageData"); + //if (endPoint == null) throw new ArgumentNullException("endPoint"); + + //cancellationToken.ThrowIfCancellationRequested(); + + //var tcs = new TaskCompletionSource(); + + //cancellationToken.Register(() => tcs.TrySetCanceled()); + + //_sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); + //_sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); + //_currentSendTaskCompletionSource = tcs; + + //var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); + + //if (!willRaiseEvent) + //{ + // _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); + //} + + //return tcs.Task; } public async Task SendWithLockAsync(byte[] buffer, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken) { ThrowIfDisposed(); - await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); + //await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { @@ -177,7 +230,7 @@ namespace Emby.Common.Implementations.Net } finally { - _sendLock.Release(); + //_sendLock.Release(); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 17c57712e..f0529c596 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -505,7 +505,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } else { - //return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + var enableHttpStream = false; + + if (enableHttpStream) + { + return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + } + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } } From 85f4f8dbc970730eba083dec1d8b586510d965a0 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 19:36:36 -0400 Subject: [PATCH 2/6] handle invalid filename chars in episode expressions --- .../FileOrganization/EpisodeFileOrganizer.cs | 46 ++++--------------- 1 file changed, 8 insertions(+), 38 deletions(-) diff --git a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs index f841b8b6b..0a9c67285 100644 --- a/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs +++ b/Emby.Server.Implementations/FileOrganization/EpisodeFileOrganizer.cs @@ -677,20 +677,7 @@ namespace Emby.Server.Implementations.FileOrganization var newPath = GetSeasonFolderPath(series, seasonNumber.Value, options); - // MAX_PATH - trailing charachter - drive component: 260 - 1 - 3 = 256 - // Usually newPath would include the drive component, but use 256 to be sure - var maxFilenameLength = 256 - newPath.Length; - - if (!newPath.EndsWith(@"\")) - { - // Remove 1 for missing backslash combining path and filename - maxFilenameLength--; - } - - // Remove additional 4 chars to prevent PathTooLongException for downloaded subtitles (eg. filename.ext.eng.srt) - maxFilenameLength -= 4; - - var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options, maxFilenameLength); + var episodeFileName = GetEpisodeFileName(sourcePath, series.Name, seasonNumber.Value, episodeNumber.Value, endingEpisodeNumber, episodeName, options); if (string.IsNullOrEmpty(episodeFileName)) { @@ -742,7 +729,7 @@ namespace Emby.Server.Implementations.FileOrganization return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName)); } - private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options, int? maxLength) + private string GetEpisodeFileName(string sourcePath, string seriesName, int seasonNumber, int episodeNumber, int? endingEpisodeNumber, string episodeTitle, TvFileOrganizationOptions options) { seriesName = _fileSystem.GetValidFilename(seriesName).Trim(); @@ -786,32 +773,15 @@ namespace Emby.Server.Implementations.FileOrganization .Replace("%0e", episodeNumber.ToString("00", _usCulture)) .Replace("%00e", episodeNumber.ToString("000", _usCulture)); - if (maxLength.HasValue && result.Contains("%#")) + if (result.Contains("%#")) { - // Substract 3 for the temp token length (%#1, %#2 or %#3) - int maxRemainingTitleLength = maxLength.Value - result.Length + 3; - string shortenedEpisodeTitle = string.Empty; - - if (maxRemainingTitleLength > 5) - { - // A title with fewer than 5 letters wouldn't be of much value - shortenedEpisodeTitle = episodeTitle.Substring(0, Math.Min(maxRemainingTitleLength, episodeTitle.Length)); - } - - result = result.Replace("%#1", shortenedEpisodeTitle) - .Replace("%#2", shortenedEpisodeTitle.Replace(" ", ".")) - .Replace("%#3", shortenedEpisodeTitle.Replace(" ", "_")); + result = result.Replace("%#1", episodeTitle) + .Replace("%#2", episodeTitle.Replace(" ", ".")) + .Replace("%#3", episodeTitle.Replace(" ", "_")); } - if (maxLength.HasValue && result.Length > maxLength.Value) - { - // There may be cases where reducing the title length may still not be sufficient to - // stay below maxLength - var msg = string.Format("Unable to generate an episode file name shorter than {0} characters to constrain to the max path limit", maxLength); - throw new Exception(msg); - } - - return result; + // Finally, call GetValidFilename again in case user customized the episode expression with any invalid filename characters + return _fileSystem.GetValidFilename(result).Trim(); } private bool IsSameEpisode(string sourcePath, string newPath) From 5477bb95d9bf994f9c68bf5384d26db41c82d5de Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 19:36:52 -0400 Subject: [PATCH 3/6] update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 834f0a05c..fc23422d7 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -49,7 +49,7 @@ namespace Emby.Common.Implementations.Net private void InitReceiveSocketAsyncEventArgs() { - var receiveBuffer = new byte[8192]; + var receiveBuffer = new byte[81920]; _receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; @@ -128,11 +128,18 @@ namespace Emby.Common.Implementations.Net _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; _currentReceiveTaskCompletionSource = tcs; - var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); - - if (!willRaiseEvent) + try { - _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); + + if (!willRaiseEvent) + { + _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + } + } + catch (Exception ex) + { + tcs.TrySetException(ex); } return tcs.Task; @@ -189,8 +196,6 @@ namespace Emby.Common.Implementations.Net taskSource.TrySetException(ex); } - //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); - return taskSource.Task; #endif //ThrowIfDisposed(); From 1a5a0d2cbbf70d5e62fdc8d371504010c7ed089c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 19:53:50 -0400 Subject: [PATCH 4/6] update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 58 +++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index fc23422d7..e44b484cb 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -119,10 +119,17 @@ namespace Emby.Common.Implementations.Net public Task ReceiveAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); - var tcs = new TaskCompletionSource(); - EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); + + var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); + state.TaskCompletionSource = tcs; + +#if NET46 + _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); + return tcs.Task; +#endif + cancellationToken.Register(() => tcs.TrySetCanceled()); _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; @@ -271,5 +278,52 @@ namespace Emby.Common.Implementations.Net return NetworkManager.ToIpEndPointInfo(endpoint); } + + private void ProcessResponse(IAsyncResult asyncResult) + { +#if NET46 + var state = asyncResult.AsyncState as AsyncReceiveState; + try + { + var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); + + var ipEndPoint = state.RemoteEndPoint as IPEndPoint; + state.TaskCompletionSource.SetResult( + new SocketReceiveResult + { + Buffer = state.Buffer, + ReceivedBytes = bytesRead, + RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), + LocalIPAddress = LocalIPAddress + } + ); + } + catch (ObjectDisposedException) + { + state.TaskCompletionSource.SetCanceled(); + } + catch (Exception ex) + { + state.TaskCompletionSource.SetException(ex); + } +#endif + } + + private class AsyncReceiveState + { + public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint) + { + this.Socket = socket; + this.RemoteEndPoint = remoteEndPoint; + } + + public EndPoint RemoteEndPoint; + public byte[] Buffer = new byte[8192]; + + public Socket Socket { get; private set; } + + public TaskCompletionSource TaskCompletionSource { get; set; } + + } } } From e004e6650099d3179da996fcccf29ffb65081c09 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 20:27:29 -0400 Subject: [PATCH 5/6] update tuner methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 5 ---- .../TunerHosts/HdHomerun/HdHomerunHost.cs | 27 ++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index e44b484cb..a97b2e668 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -125,11 +125,6 @@ namespace Emby.Common.Implementations.Net var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; -#if NET46 - _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); - return tcs.Task; -#endif - cancellationToken.Register(() => tcs.TrySetCanceled()); _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index f0529c596..2fac96169 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -20,6 +20,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Net; +using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; private readonly INetworkManager _networkManager; + private readonly IEnvironmentInfo _environment; - public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) + public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder) { _httpClient = httpClient; @@ -39,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _appHost = appHost; _socketFactory = socketFactory; _networkManager = networkManager; + _environment = environment; } public string Name @@ -503,17 +506,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } - else + + // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet + var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX || + _environment.OperatingSystem == OperatingSystem.BSD; + + if (enableHttpStream) { - var enableHttpStream = false; + mediaSource.Protocol = MediaProtocol.Http; - if (enableHttpStream) + var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId; + + // If raw was used, the tuner doesn't support params + if (!string.IsNullOrWhiteSpace(profile) + && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { - return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + httpUrl += "?transcode=" + profile; } + mediaSource.Path = httpUrl; - return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); } + + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } public async Task Validate(TunerHostInfo info) From c071ec551b61a843e83c8b4c9af5e0c50ea9127e Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 20:27:44 -0400 Subject: [PATCH 6/6] 3.2.8.15 --- SharedVersion.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SharedVersion.cs b/SharedVersion.cs index f7f49dc75..d173d9ec5 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.8.14")] +[assembly: AssemblyVersion("3.2.8.15")]