From b38b7a706268fe5d92d8cbe703a188b58ed7ec4d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:08:23 -0400 Subject: [PATCH 1/7] rework filestream --- .../Net/NetAcceptSocket.cs | 2 +- .../Net/SocketFactory.cs | 27 ++++++++++++++++--- .../HttpServer/FileWriter.cs | 7 +++-- .../HttpServer/HttpResultFactory.cs | 5 ++-- .../SocketSharp/WebSocketSharpResponse.cs | 4 +-- .../Library/LibraryManager.cs | 27 ++++++++++++------- MediaBrowser.Api/StartupWizardService.cs | 1 + .../Entities/Audio/MusicArtist.cs | 7 ++++- .../Entities/Audio/MusicGenre.cs | 7 ++++- MediaBrowser.Controller/Entities/GameGenre.cs | 7 ++++- MediaBrowser.Controller/Entities/Genre.cs | 7 ++++- MediaBrowser.Controller/Entities/Person.cs | 7 ++++- MediaBrowser.Controller/Entities/Studio.cs | 7 ++++- MediaBrowser.Controller/Entities/Year.cs | 7 ++++- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 ++ .../Configuration/ServerConfiguration.cs | 1 + MediaBrowser.Model/Net/ISocketFactory.cs | 2 ++ MediaBrowser.Model/Services/IRequest.cs | 3 ++- .../Net/HttpListenerResponse.cs | 4 +-- .../Net/ResponseStream.cs | 8 +++--- 20 files changed, 108 insertions(+), 34 deletions(-) diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index e21ffe553..3721709e6 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -100,7 +100,7 @@ namespace Emby.Common.Implementations.Net #if NET46 public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) { - var options = TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket | TransmitFileOptions.UseKernelApc; + var options = TransmitFileOptions.UseKernelApc; var completionSource = new TaskCompletionSource(); diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index 021613e57..0f4306a6b 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -97,10 +97,31 @@ namespace Emby.Common.Implementations.Net } } + public ISocket CreateUdpBroadcastSocket(int localPort) + { + if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); + + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); + try + { + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + + return new UdpSocket(retVal, localPort, IPAddress.Any); + } + catch + { + if (retVal != null) + retVal.Dispose(); + + throw; + } + } + /// - /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. - /// - /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. + /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. + /// + /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index b80a40962..d230a9b91 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -27,6 +27,8 @@ namespace Emby.Server.Implementations.HttpServer private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); public List Cookies { get; private set; } + public FileShareMode FileShare { get; set; } + /// /// The _options /// @@ -69,6 +71,7 @@ namespace Emby.Server.Implementations.HttpServer SetRangeValues(); } + FileShare = FileShareMode.Read; Cookies = new List(); } @@ -153,11 +156,11 @@ namespace Emby.Server.Implementations.HttpServer if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1)) { Logger.Info("Transmit file {0}", Path); - await response.TransmitFile(Path, 0, 0, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false); return; } - await response.TransmitFile(Path, RangeStart, RangeEnd, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, RangeStart, RangeEnd, FileShare, cancellationToken).ConfigureAwait(false); } finally { diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index e3f105941..310161d41 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -556,12 +556,13 @@ namespace Emby.Server.Implementations.HttpServer { var rangeHeader = requestContext.Headers.Get("Range"); - if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path) && options.FileShare == FileShareMode.Read) + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) { OnComplete = options.OnComplete, - OnError = options.OnError + OnError = options.OnError, + FileShare = options.FileShare }; } diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs index a497ee715..fd30b227f 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp { } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return _response.TransmitFile(path, offset, count, cancellationToken); + return _response.TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f7706db47..026486efc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -513,6 +513,11 @@ namespace Emby.Server.Implementations.Library } public Guid GetNewItemId(string key, Type type) + { + return GetNewItemIdInternal(key, type, false); + } + + private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive) { if (string.IsNullOrWhiteSpace(key)) { @@ -531,7 +536,7 @@ namespace Emby.Server.Implementations.Library .Replace("/", "\\"); } - if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) + if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) { key = key.ToLower(); } @@ -865,7 +870,7 @@ namespace Emby.Server.Implementations.Library /// Task{Person}. public Person GetPerson(string name) { - return CreateItemByName(Person.GetPath(name), name); + return CreateItemByName(Person.GetPath, name); } /// @@ -875,7 +880,7 @@ namespace Emby.Server.Implementations.Library /// Task{Studio}. public Studio GetStudio(string name) { - return CreateItemByName(Studio.GetPath(name), name); + return CreateItemByName(Studio.GetPath, name); } /// @@ -885,7 +890,7 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public Genre GetGenre(string name) { - return CreateItemByName(Genre.GetPath(name), name); + return CreateItemByName(Genre.GetPath, name); } /// @@ -895,7 +900,7 @@ namespace Emby.Server.Implementations.Library /// Task{MusicGenre}. public MusicGenre GetMusicGenre(string name) { - return CreateItemByName(MusicGenre.GetPath(name), name); + return CreateItemByName(MusicGenre.GetPath, name); } /// @@ -905,7 +910,7 @@ namespace Emby.Server.Implementations.Library /// Task{GameGenre}. public GameGenre GetGameGenre(string name) { - return CreateItemByName(GameGenre.GetPath(name), name); + return CreateItemByName(GameGenre.GetPath, name); } /// @@ -923,7 +928,7 @@ namespace Emby.Server.Implementations.Library var name = value.ToString(CultureInfo.InvariantCulture); - return CreateItemByName(Year.GetPath(name), name); + return CreateItemByName(Year.GetPath, name); } /// @@ -933,10 +938,10 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public MusicArtist GetArtist(string name) { - return CreateItemByName(MusicArtist.GetPath(name), name); + return CreateItemByName(MusicArtist.GetPath, name); } - private T CreateItemByName(string path, string name) + private T CreateItemByName(Func getPathFn, string name) where T : BaseItem, new() { if (typeof(T) == typeof(MusicArtist)) @@ -957,7 +962,9 @@ namespace Emby.Server.Implementations.Library } } - var id = GetNewItemId(path, typeof(T)); + var path = getPathFn(name); + var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds; + var id = GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); var item = GetItemById(id) as T; diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index e010f122c..02154b98d 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Api config.EnableSeriesPresentationUniqueKey = true; config.EnableLocalizedGuids = true; config.EnableSimpleArtistDetection = true; + config.EnableNormalizedItemByNameIds = true; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 20b2529c0..8d83f8a35 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -289,7 +289,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 74679b474..e26e0dfce 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -118,7 +118,12 @@ namespace MediaBrowser.Controller.Entities.Audio return LibraryManager.GetItemList(query); } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index 22a8675c5..4187167b9 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -96,7 +96,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 1b746ae51..9769efdd0 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -108,7 +108,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index ee1aea938..b68681d36 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -133,7 +133,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validFilename = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index b8ad691a9..2e5e6ce43 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -114,7 +114,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 75fb69435..b352950a0 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -122,7 +122,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 5615649c2..af1c0d12e 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -44,6 +44,8 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + + Task> DiscoverDevices(int discoveryDurationMs); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c2b1e3c89..0562d0ac5 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableHttps { get; set; } public bool EnableSeriesPresentationUniqueKey { get; set; } public bool EnableLocalizedGuids { get; set; } + public bool EnableNormalizedItemByNameIds { get; set; } /// /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 4b70f3362..e7dbf6cb1 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -14,6 +14,8 @@ namespace MediaBrowser.Model.Net /// A implementation. ISocket CreateUdpSocket(int localPort); + ISocket CreateUdpBroadcastSocket(int localPort); + ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); /// diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 40cef4ec0..115ba25ce 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -4,6 +4,7 @@ using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.IO; namespace MediaBrowser.Model.Services { @@ -154,6 +155,6 @@ namespace MediaBrowser.Model.Services //Add Metadata to Response Dictionary Items { get; } - Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken); + Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken); } } diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index d8011f05e..d9f91c0cc 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -515,9 +515,9 @@ namespace SocketHttpListener.Net cookies.Add(cookie); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, cancellationToken); + return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } \ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index ccc0efc55..19821f954 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -307,13 +307,13 @@ namespace SocketHttpListener.Net throw new NotSupportedException(); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked) //{ // return TransmitFileOverSocket(path, offset, count, cancellationToken); //} - return TransmitFileManaged(path, offset, count, cancellationToken); + return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } private readonly byte[] _emptyBuffer = new byte[] { }; @@ -334,7 +334,7 @@ namespace SocketHttpListener.Net await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false); } - private async Task TransmitFileManaged(string path, long offset, long count, CancellationToken cancellationToken) + private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { var chunked = response.SendChunked; @@ -343,7 +343,7 @@ namespace SocketHttpListener.Net await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false); } - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true)) { if (offset > 0) { From 0650be4780afb8a8a32b2819596ca37047e5a90b Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:08:49 -0400 Subject: [PATCH 2/7] rework tuner discovery --- .../Emby.Server.Implementations.csproj | 1 - .../HdHomerun/HdHomerunDiscovery.cs | 158 ------------------ .../TunerHosts/HdHomerun/HdHomerunHost.cs | 71 +++++++- .../LiveTv/TunerHosts/M3UTunerHost.cs | 5 + 4 files changed, 75 insertions(+), 160 deletions(-) delete mode 100644 Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index d873cd77f..ecd86d507 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -171,7 +171,6 @@ - diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs deleted file mode 100644 index 336469c50..000000000 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunDiscovery.cs +++ /dev/null @@ -1,158 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Dlna; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Plugins; -using MediaBrowser.Model.Extensions; -using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using MediaBrowser.Common.Net; -using MediaBrowser.Model.Dlna; -using MediaBrowser.Model.Events; -using MediaBrowser.Model.Serialization; - -namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun -{ - public class HdHomerunDiscovery : IServerEntryPoint - { - private readonly IDeviceDiscovery _deviceDiscovery; - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; - private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _json; - - public HdHomerunDiscovery(IDeviceDiscovery deviceDiscovery, IServerConfigurationManager config, ILogger logger, ILiveTvManager liveTvManager, IHttpClient httpClient, IJsonSerializer json) - { - _deviceDiscovery = deviceDiscovery; - _config = config; - _logger = logger; - _liveTvManager = liveTvManager; - _httpClient = httpClient; - _json = json; - } - - public void Run() - { - _deviceDiscovery.DeviceDiscovered += _deviceDiscovery_DeviceDiscovered; - } - - void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs e) - { - string server = null; - var info = e.Argument; - - if (info.Headers.TryGetValue("SERVER", out server) && server.IndexOf("HDHomeRun", StringComparison.OrdinalIgnoreCase) != -1) - { - string location; - if (info.Headers.TryGetValue("Location", out location)) - { - //_logger.Debug("HdHomerun found at {0}", location); - - // Just get the beginning of the url - Uri uri; - if (Uri.TryCreate(location, UriKind.Absolute, out uri)) - { - var apiUrl = location.Replace(uri.LocalPath, String.Empty, StringComparison.OrdinalIgnoreCase) - .TrimEnd('/'); - - //_logger.Debug("HdHomerun api url: {0}", apiUrl); - AddDevice(apiUrl); - } - } - } - } - - private async void AddDevice(string url) - { - await _semaphore.WaitAsync().ConfigureAwait(false); - - try - { - var options = GetConfiguration(); - - if (options.TunerHosts.Any(i => - string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) && - UriEquals(i.Url, url))) - { - return; - } - - // Strip off the port - url = new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped).TrimEnd('/'); - - // Test it by pulling down the lineup - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = string.Format("{0}/discover.json", url), - CancellationToken = CancellationToken.None, - BufferContent = false - })) - { - var response = _json.DeserializeFromStream(stream); - - var existing = GetConfiguration().TunerHosts - .FirstOrDefault(i => string.Equals(i.Type, HdHomerunHost.DeviceType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.DeviceId, response.DeviceID, StringComparison.OrdinalIgnoreCase)); - - if (existing == null) - { - await _liveTvManager.SaveTunerHost(new TunerHostInfo - { - Type = HdHomerunHost.DeviceType, - Url = url, - DeviceId = response.DeviceID - - }).ConfigureAwait(false); - } - else - { - if (!string.Equals(existing.Url, url, StringComparison.OrdinalIgnoreCase)) - { - existing.Url = url; - await _liveTvManager.SaveTunerHost(existing).ConfigureAwait(false); - } - } - } - } - catch (Exception ex) - { - _logger.ErrorException("Error saving device", ex); - } - finally - { - _semaphore.Release(); - } - } - - private bool UriEquals(string savedUri, string location) - { - return string.Equals(NormalizeUrl(location), NormalizeUrl(savedUri), StringComparison.OrdinalIgnoreCase); - } - - private string NormalizeUrl(string url) - { - if (!url.StartsWith("http", StringComparison.OrdinalIgnoreCase)) - { - url = "http://" + url; - } - - url = url.TrimEnd('/'); - - // Strip off the port - return new Uri(url).GetComponents(UriComponents.AbsoluteUri & ~UriComponents.Port, UriFormat.UriEscaped); - } - - private LiveTvOptions GetConfiguration() - { - return _config.GetConfiguration("livetv"); - } - - public void Dispose() - { - } - } -} diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 62385e172..487aa7ca9 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -587,7 +587,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var hdhomerunChannel = channelInfo as HdHomerunChannelInfo; if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) - { + { var mediaSource = GetLegacyMediaSource(info, hdhrId, channelInfo); var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); @@ -652,5 +652,74 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public string LineupURL { get; set; } public int TunerCount { get; set; } } + + public async Task> DiscoverDevices(int discoveryDurationMs) + { + var cancellationToken = new CancellationTokenSource(discoveryDurationMs).Token; + var list = new List(); + + // Create udp broadcast discovery message + byte[] discBytes = { 0, 2, 0, 12, 1, 4, 255, 255, 255, 255, 2, 4, 255, 255, 255, 255, 115, 204, 125, 143 }; + using (var udpClient = _socketFactory.CreateUdpBroadcastSocket(0)) + { + // Need a way to set the Receive timeout on the socket otherwise this might never timeout? + try + { + await udpClient.SendAsync(discBytes, discBytes.Length, new IpEndPointInfo(new IpAddressInfo("255.255.255.255", IpAddressFamily.InterNetwork), 65001), cancellationToken); + while (!cancellationToken.IsCancellationRequested) + { + var response = await udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); + var deviceIp = response.RemoteEndPoint.IpAddress.Address; + + // check to make sure we have enough bytes received to be a valid message and make sure the 2nd byte is the discover reply byte + if (response.ReceivedBytes > 13 && response.Buffer[1] == 3) + { + var deviceAddress = "http://" + deviceIp; + + var info = await TryGetTunerHostInfo(deviceAddress, cancellationToken).ConfigureAwait(false); + + if (info != null) + { + list.Add(info); + } + } + } + + } + catch (OperationCanceledException) + { + } + catch + { + // Socket timeout indicates all messages have been received. + } + } + + return list; + } + + private async Task TryGetTunerHostInfo(string url, CancellationToken cancellationToken) + { + var hostInfo = new TunerHostInfo + { + Type = Type, + Url = url + }; + + try + { + var modelInfo = await GetModelInfo(hostInfo, false, cancellationToken).ConfigureAwait(false); + + hostInfo.DeviceId = modelInfo.DeviceID; + + return hostInfo; + } + catch + { + // logged at lower levels + } + + return null; + } } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index a939cec7b..a09da3e53 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -176,5 +176,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { return Task.FromResult(true); } + + public Task> DiscoverDevices(int discoveryDurationMs) + { + return Task.FromResult(new List()); + } } } \ No newline at end of file From f05dc08c06a386aca052a2abb981e309bd005d91 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:49:10 -0400 Subject: [PATCH 3/7] update tuner discovery --- Emby.Common.Implementations/Net/UdpSocket.cs | 18 ++++--- .../LiveTv/EmbyTV/EmbyTV.cs | 54 +++++++++++++++++++ .../LiveTv/LiveTvManager.cs | 2 + .../TunerHosts/HdHomerun/HdHomerunHost.cs | 4 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 +- 6 files changed, 70 insertions(+), 12 deletions(-) diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 8e2a1da6f..b85245ba1 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -60,6 +60,8 @@ namespace Emby.Common.Implementations.Net var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; + cancellationToken.Register(() => tcs.TrySetCanceled()); + #if NETSTANDARD1_6 _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer), SocketFlags.None, state.RemoteEndPoint) .ContinueWith((task, asyncState) => @@ -160,7 +162,7 @@ namespace Emby.Common.Implementations.Net var bytesRead = receiveData(); var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.SetResult( + state.TaskCompletionSource.TrySetResult( new SocketReceiveResult { Buffer = state.Buffer, @@ -172,18 +174,18 @@ namespace Emby.Common.Implementations.Net } catch (ObjectDisposedException) { - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (SocketException se) { if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) - state.TaskCompletionSource.SetException(se); + state.TaskCompletionSource.TrySetException(se); else - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (Exception ex) { - state.TaskCompletionSource.SetException(ex); + state.TaskCompletionSource.TrySetException(ex); } } @@ -206,7 +208,7 @@ namespace Emby.Common.Implementations.Net var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.SetResult( + state.TaskCompletionSource.TrySetResult( new SocketReceiveResult { Buffer = state.Buffer, @@ -218,11 +220,11 @@ namespace Emby.Common.Implementations.Net } catch (ObjectDisposedException) { - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (Exception ex) { - state.TaskCompletionSource.SetException(ex); + state.TaskCompletionSource.TrySetException(ex); } #endif } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index d9060a066..5c7c28cc8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2542,6 +2542,60 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public ProgramInfo Program { get; set; } public CancellationTokenSource CancellationTokenSource { get; set; } } + + public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken) + { + foreach (var host in _liveTvManager.TunerHosts) + { + await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false); + } + } + + private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken) + { + var discoveredDevices = await DiscoverDevices(host, 2000, cancellationToken).ConfigureAwait(false); + + var configuredDevices = GetConfiguration().TunerHosts + .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var device in discoveredDevices) + { + var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase)); + + if (configuredDevice != null) + { + if (!string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase)) + { + _logger.Info("Tuner url has changed from {0} to {1}", configuredDevice.Url, device.Url); + + configuredDevice.Url = device.Url; + await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false); + } + } + } + } + + private async Task> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken) + { + try + { + var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false); + + foreach (var device in discoveredDevices) + { + _logger.Info("Discovered tuner device {0} at {1}", host.Name, device.Url); + } + + return discoveredDevices; + } + catch (Exception ex) + { + _logger.ErrorException("Error discovering tuner devices", ex); + + return new List(); + } + } } public static class ConfigurationExtension { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 618cd1d45..fc2e0ce72 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1180,6 +1180,8 @@ namespace Emby.Server.Implementations.LiveTv { EmbyTV.EmbyTV.Current.CreateRecordingFolders(); + await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false); + var numComplete = 0; double progressPerService = _services.Count == 0 ? 0 diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 487aa7ca9..6b80cec19 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -653,9 +653,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public int TunerCount { get; set; } } - public async Task> DiscoverDevices(int discoveryDurationMs) + public async Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken) { - var cancellationToken = new CancellationTokenSource(discoveryDurationMs).Token; + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token; var list = new List(); // Create udp broadcast discovery message diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index a09da3e53..cc0ae0983 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(true); } - public Task> DiscoverDevices(int discoveryDurationMs) + public Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken) { return Task.FromResult(new List()); } diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index af1c0d12e..fc344298b 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.LiveTv /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); - Task> DiscoverDevices(int discoveryDurationMs); + Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken); } public interface IConfigurableTunerHost { From 2ff7608b7ad28bd2ba20d7ff95513c96656a42d4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:56:41 -0400 Subject: [PATCH 4/7] remove tuner host enabled property --- Emby.Server.Implementations/LiveTv/LiveTvManager.cs | 4 ++-- .../LiveTv/RefreshChannelsScheduledTask.cs | 2 +- .../LiveTv/TunerHosts/BaseTunerHost.cs | 2 +- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 7 +------ MediaBrowser.Api/StartupWizardService.cs | 1 - MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 2 -- 6 files changed, 5 insertions(+), 13 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index fc2e0ce72..7904024bf 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -2750,7 +2750,7 @@ namespace Emby.Server.Implementations.LiveTv private bool IsLiveTvEnabled(User user) { - return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count(i => i.IsEnabled) > 0); + return user.Policy.EnableLiveTvAccess && (Services.Count > 1 || GetConfiguration().TunerHosts.Count > 0); } public IEnumerable GetEnabledUsers() @@ -2988,7 +2988,7 @@ namespace Emby.Server.Implementations.LiveTv if (string.Equals(feature, "dvr-l", StringComparison.OrdinalIgnoreCase)) { var config = GetConfiguration(); - if (config.TunerHosts.Count(i => i.IsEnabled) > 0 && + if (config.TunerHosts.Count > 0 && config.ListingProviders.Count(i => (i.EnableAllTuners || i.EnabledTuners.Length > 0) && string.Equals(i.Type, SchedulesDirect.TypeName, StringComparison.OrdinalIgnoreCase)) > 0) { return Task.FromResult(new MBRegistrationRecord diff --git a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs index f2806292d..5582d8f35 100644 --- a/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs +++ b/Emby.Server.Implementations/LiveTv/RefreshChannelsScheduledTask.cs @@ -62,7 +62,7 @@ namespace Emby.Server.Implementations.LiveTv public bool IsHidden { - get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count(i => i.IsEnabled) == 0; } + get { return _liveTvManager.Services.Count == 1 && GetConfiguration().TunerHosts.Count == 0; } } public bool IsEnabled diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs index 5ac3812b0..79fa9bb61 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/BaseTunerHost.cs @@ -69,7 +69,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts protected virtual List GetTunerHosts() { return GetConfiguration().TunerHosts - .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) + .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase)) .ToList(); } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 6b80cec19..dbcabd174 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -215,7 +215,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var list = new List(); foreach (var host in GetConfiguration().TunerHosts - .Where(i => i.IsEnabled && string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))) + .Where(i => string.Equals(i.Type, Type, StringComparison.OrdinalIgnoreCase))) { try { @@ -605,11 +605,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public async Task Validate(TunerHostInfo info) { - if (!info.IsEnabled) - { - return; - } - lock (_modelCache) { _modelCache.Clear(); diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 02154b98d..0155ef31b 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -188,7 +188,6 @@ namespace MediaBrowser.Api // Add tuner await _liveTvManager.SaveTunerHost(new TunerHostInfo { - IsEnabled = true, Type = request.LiveTvTunerType, Url = request.LiveTvTunerPath diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index 79a484a46..c595f9cea 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -48,12 +48,10 @@ namespace MediaBrowser.Model.LiveTv public string DeviceId { get; set; } public bool ImportFavoritesOnly { get; set; } public bool AllowHWTranscoding { get; set; } - public bool IsEnabled { get; set; } public bool EnableTvgId { get; set; } public TunerHostInfo() { - IsEnabled = true; AllowHWTranscoding = true; } } From a8fc4804893cb1ca8d54cd85019f53f621b97e91 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 14:57:45 -0400 Subject: [PATCH 5/7] rework tuner setup --- .../LiveTv/EmbyTV/EmbyTV.cs | 2 +- .../LiveTv/LiveTvManager.cs | 54 +++----------- MediaBrowser.Api/LiveTv/LiveTvService.cs | 53 +++++-------- MediaBrowser.Api/StartupWizardService.cs | 74 +------------------ .../LiveTv/ILiveTvManager.cs | 10 +-- MediaBrowser.Model/LiveTv/LiveTvOptions.cs | 1 + 6 files changed, 36 insertions(+), 158 deletions(-) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 5c7c28cc8..a7ccafd69 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2553,7 +2553,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken) { - var discoveredDevices = await DiscoverDevices(host, 2000, cancellationToken).ConfigureAwait(false); + var discoveredDevices = await DiscoverDevices(host, 3000, cancellationToken).ConfigureAwait(false); var configuredDevices = GetConfiguration().TunerHosts .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 7904024bf..de39d3838 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -150,6 +150,16 @@ namespace Emby.Server.Implementations.LiveTv get { return _listingProviders; } } + public List GetTunerHostTypes() + { + return _tunerHosts.OrderBy(i => i.Name).Select(i => new NameIdPair + { + Name = i.Name, + Id = i.Type + + }).ToList(); + } + void service_DataSourceChanged(object sender, EventArgs e) { if (!_isDisposed) @@ -3002,50 +3012,6 @@ namespace Emby.Server.Implementations.LiveTv return _security.GetRegistrationStatus(feature); } - public List GetSatIniMappings() - { - return new List(); - //var names = GetType().Assembly.GetManifestResourceNames().Where(i => i.IndexOf("SatIp.ini", StringComparison.OrdinalIgnoreCase) != -1).ToList(); - - //return names.Select(GetSatIniMappings).Where(i => i != null).DistinctBy(i => i.Value.Split('|')[0]).ToList(); - } - - public NameValuePair GetSatIniMappings(string resource) - { - return new NameValuePair(); - //using (var stream = GetType().Assembly.GetManifestResourceStream(resource)) - //{ - // using (var reader = new StreamReader(stream)) - // { - // var parser = new StreamIniDataParser(); - // IniData data = parser.ReadData(reader); - - // var satType1 = data["SATTYPE"]["1"]; - // var satType2 = data["SATTYPE"]["2"]; - - // if (string.IsNullOrWhiteSpace(satType2)) - // { - // return null; - // } - - // var srch = "SatIp.ini."; - // var filename = Path.GetFileName(resource); - - // return new NameValuePair - // { - // Name = satType1 + " " + satType2, - // Value = satType2 + "|" + filename.Substring(filename.IndexOf(srch) + srch.Length) - // }; - // } - //} - } - - public Task> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken) - { - return Task.FromResult(new List()); - //return new TunerHosts.SatIp.ChannelScan(_logger).Scan(info, cancellationToken); - } - public Task> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken) { var info = GetConfiguration().ListingProviders.First(i => string.Equals(i.Id, id, StringComparison.OrdinalIgnoreCase)); diff --git a/MediaBrowser.Api/LiveTv/LiveTvService.cs b/MediaBrowser.Api/LiveTv/LiveTvService.cs index dc4e57155..639021762 100644 --- a/MediaBrowser.Api/LiveTv/LiveTvService.cs +++ b/MediaBrowser.Api/LiveTv/LiveTvService.cs @@ -582,13 +582,13 @@ namespace MediaBrowser.Api.LiveTv } [Route("/LiveTv/ListingProviders/Default", "GET")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class GetDefaultListingProvider : ListingsProviderInfo, IReturn { } [Route("/LiveTv/ListingProviders", "POST", Summary = "Adds a listing provider")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class AddListingProvider : ListingsProviderInfo, IReturn { public bool ValidateLogin { get; set; } @@ -596,7 +596,7 @@ namespace MediaBrowser.Api.LiveTv } [Route("/LiveTv/ListingProviders", "DELETE", Summary = "Deletes a listing provider")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class DeleteListingProvider : IReturnVoid { [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "DELETE")] @@ -604,7 +604,7 @@ namespace MediaBrowser.Api.LiveTv } [Route("/LiveTv/ListingProviders/Lineups", "GET", Summary = "Gets available lineups")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class GetLineups : IReturn> { [ApiMember(Name = "Id", Description = "Provider id", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -621,13 +621,13 @@ namespace MediaBrowser.Api.LiveTv } [Route("/LiveTv/ListingProviders/SchedulesDirect/Countries", "GET", Summary = "Gets available lineups")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class GetSchedulesDirectCountries { } [Route("/LiveTv/ChannelMappingOptions")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class GetChannelMappingOptions { [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -635,7 +635,7 @@ namespace MediaBrowser.Api.LiveTv } [Route("/LiveTv/ChannelMappings")] - [Authenticated(AllowBeforeStartupWizard = true)] + [Authenticated] public class SetChannelMapping { [ApiMember(Name = "Id", Description = "Provider id", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] @@ -660,20 +660,6 @@ namespace MediaBrowser.Api.LiveTv public string Feature { get; set; } } - [Route("/LiveTv/TunerHosts/Satip/IniMappings", "GET", Summary = "Gets available mappings")] - [Authenticated(AllowBeforeStartupWizard = true)] - public class GetSatIniMappings : IReturn> - { - - } - - [Route("/LiveTv/TunerHosts/Satip/ChannelScan", "GET", Summary = "Scans for available channels")] - [Authenticated(AllowBeforeStartupWizard = true)] - public class GetSatChannnelScanResult : TunerHostInfo - { - - } - [Route("/LiveTv/LiveStreamFiles/{Id}/stream.{Container}", "GET", Summary = "Gets a live tv channel")] public class GetLiveStreamFile { @@ -687,6 +673,13 @@ namespace MediaBrowser.Api.LiveTv public string Id { get; set; } } + [Route("/LiveTv/TunerHosts/Types", "GET")] + [Authenticated] + public class GetTunerHostTypes : IReturn> + { + + } + public class LiveTvService : BaseApiService { private readonly ILiveTvManager _liveTvManager; @@ -712,6 +705,12 @@ namespace MediaBrowser.Api.LiveTv _sessionContext = sessionContext; } + public object Get(GetTunerHostTypes request) + { + var list = _liveTvManager.GetTunerHostTypes(); + return ToOptimizedResult(list); + } + public object Get(GetLiveRecordingFile request) { var path = _liveTvManager.GetEmbyTvActiveRecordingPath(request.Id); @@ -749,13 +748,6 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(new ListingsProviderInfo()); } - public async Task Get(GetSatChannnelScanResult request) - { - var result = await _liveTvManager.GetSatChannelScanResult(request, CancellationToken.None).ConfigureAwait(false); - - return ToOptimizedResult(result); - } - public async Task Get(GetLiveTvRegistrationInfo request) { var result = await _liveTvManager.GetRegistrationInfo(request.Feature).ConfigureAwait(false); @@ -803,11 +795,6 @@ namespace MediaBrowser.Api.LiveTv return ToOptimizedResult(result); } - public object Get(GetSatIniMappings request) - { - return ToOptimizedResult(_liveTvManager.GetSatIniMappings()); - } - public async Task Get(GetSchedulesDirectCountries request) { // https://json.schedulesdirect.org/20141201/available/countries diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 0155ef31b..b0f52dd85 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -1,12 +1,9 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Controller; +using MediaBrowser.Controller; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Connect; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.LiveTv; using System; using System.Linq; using System.Threading.Tasks; @@ -52,16 +49,14 @@ namespace MediaBrowser.Api private readonly IServerApplicationHost _appHost; private readonly IUserManager _userManager; private readonly IConnectManager _connectManager; - private readonly ILiveTvManager _liveTvManager; private readonly IMediaEncoder _mediaEncoder; - public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, ILiveTvManager liveTvManager, IMediaEncoder mediaEncoder) + public StartupWizardService(IServerConfigurationManager config, IServerApplicationHost appHost, IUserManager userManager, IConnectManager connectManager, IMediaEncoder mediaEncoder) { _config = config; _appHost = appHost; _userManager = userManager; _connectManager = connectManager; - _liveTvManager = liveTvManager; _mediaEncoder = mediaEncoder; } @@ -92,20 +87,6 @@ namespace MediaBrowser.Api PreferredMetadataLanguage = _config.Configuration.PreferredMetadataLanguage }; - var tvConfig = GetLiveTVConfiguration(); - - if (tvConfig.TunerHosts.Count > 0) - { - result.LiveTvTunerPath = tvConfig.TunerHosts[0].Url; - result.LiveTvTunerType = tvConfig.TunerHosts[0].Type; - } - - if (tvConfig.ListingProviders.Count > 0) - { - result.LiveTvGuideProviderId = tvConfig.ListingProviders[0].Id; - result.LiveTvGuideProviderType = tvConfig.ListingProviders[0].Type; - } - return result; } @@ -129,9 +110,6 @@ namespace MediaBrowser.Api _config.Configuration.MetadataCountryCode = request.MetadataCountryCode; _config.Configuration.PreferredMetadataLanguage = request.PreferredMetadataLanguage; _config.SaveConfiguration(); - - var task = UpdateTuners(request); - Task.WaitAll(task); } public object Get(GetStartupUser request) @@ -166,50 +144,6 @@ namespace MediaBrowser.Api return result; } - - private async Task UpdateTuners(UpdateStartupConfiguration request) - { - var config = GetLiveTVConfiguration(); - var save = false; - - if (string.IsNullOrWhiteSpace(request.LiveTvTunerPath) || - string.IsNullOrWhiteSpace(request.LiveTvTunerType)) - { - if (config.TunerHosts.Count > 0) - { - config.TunerHosts.Clear(); - save = true; - } - } - else - { - if (!config.TunerHosts.Any(i => string.Equals(i.Type, request.LiveTvTunerType, StringComparison.OrdinalIgnoreCase) && string.Equals(i.Url, request.LiveTvTunerPath, StringComparison.OrdinalIgnoreCase))) - { - // Add tuner - await _liveTvManager.SaveTunerHost(new TunerHostInfo - { - Type = request.LiveTvTunerType, - Url = request.LiveTvTunerPath - - }).ConfigureAwait(false); - } - } - - if (save) - { - SaveLiveTVConfiguration(config); - } - } - - private void SaveLiveTVConfiguration(LiveTvOptions config) - { - _config.SaveConfiguration("livetv", config); - } - - private LiveTvOptions GetLiveTVConfiguration() - { - return _config.GetConfiguration("livetv"); - } } public class StartupConfiguration @@ -217,10 +151,6 @@ namespace MediaBrowser.Api public string UICulture { get; set; } public string MetadataCountryCode { get; set; } public string PreferredMetadataLanguage { get; set; } - public string LiveTvTunerType { get; set; } - public string LiveTvTunerPath { get; set; } - public string LiveTvGuideProviderId { get; set; } - public string LiveTvGuideProviderType { get; set; } } public class StartupInfo diff --git a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs index a908d2d3f..b3467fbbc 100644 --- a/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs +++ b/MediaBrowser.Controller/LiveTv/ILiveTvManager.cs @@ -376,19 +376,13 @@ namespace MediaBrowser.Controller.LiveTv /// Task. Task OnRecordingFileDeleted(BaseItem recording); - /// - /// Gets the sat ini mappings. - /// - /// List<NameValuePair>. - List GetSatIniMappings(); - - Task> GetSatChannelScanResult(TunerHostInfo info, CancellationToken cancellationToken); - Task> GetChannelsForListingsProvider(string id, CancellationToken cancellationToken); Task> GetChannelsFromListingsProviderData(string id, CancellationToken cancellationToken); List ListingProviders { get; } + List GetTunerHostTypes(); + event EventHandler> SeriesTimerCancelled; event EventHandler> TimerCancelled; event EventHandler> TimerCreated; diff --git a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs index c595f9cea..6a0fdede3 100644 --- a/MediaBrowser.Model/LiveTv/LiveTvOptions.cs +++ b/MediaBrowser.Model/LiveTv/LiveTvOptions.cs @@ -46,6 +46,7 @@ namespace MediaBrowser.Model.LiveTv public string Url { get; set; } public string Type { get; set; } public string DeviceId { get; set; } + public string FriendlyName { get; set; } public bool ImportFavoritesOnly { get; set; } public bool AllowHWTranscoding { get; set; } public bool EnableTvgId { get; set; } From 203fc64a9752f50989a04d4c6c173fe13a9dcd14 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 14:58:00 -0400 Subject: [PATCH 6/7] improve clean db task --- .../Data/CleanDatabaseScheduledTask.cs | 32 ++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs index 2819a249f..0096f2284 100644 --- a/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs +++ b/Emby.Server.Implementations/Data/CleanDatabaseScheduledTask.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Configuration; using MediaBrowser.Model.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities.Audio; @@ -22,13 +23,15 @@ namespace Emby.Server.Implementations.Data private readonly IItemRepository _itemRepo; private readonly ILogger _logger; private readonly IFileSystem _fileSystem; + private readonly IApplicationPaths _appPaths; - public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem) + public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths) { _libraryManager = libraryManager; _itemRepo = itemRepo; _logger = logger; _fileSystem = fileSystem; + _appPaths = appPaths; } public string Name @@ -150,13 +153,27 @@ namespace Emby.Server.Implementations.Data try { - if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path)) + var isPathInLibrary = false; + + if (allLibraryPaths.Any(i => path.StartsWith(i, StringComparison.Ordinal)) || + allLibraryPaths.Contains(path, StringComparer.Ordinal) || + path.StartsWith(_appPaths.ProgramDataPath, StringComparison.Ordinal)) { - continue; + isPathInLibrary = true; + + if (_fileSystem.FileExists(path) || _fileSystem.DirectoryExists(path)) + { + continue; + } } var libraryItem = _libraryManager.GetItemById(item.Item1); + if (libraryItem == null) + { + continue; + } + if (libraryItem.IsTopParent) { continue; @@ -180,7 +197,14 @@ namespace Emby.Server.Implementations.Data continue; } - _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); + if (isPathInLibrary) + { + _logger.Info("Deleting item from database {0} because path no longer exists. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); + } + else + { + _logger.Info("Deleting item from database {0} because path is no longer in the server library. type: {1} path: {2}", libraryItem.Name, libraryItem.GetType().Name, libraryItemPath ?? string.Empty); + } await libraryItem.OnFileDeleted().ConfigureAwait(false); } From a9b61af1549770b5a3c613c6b552f8bb698e9870 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 15:01:20 -0400 Subject: [PATCH 7/7] 3.2.7.4 --- Emby.Drawing/ImageProcessor.cs | 20 ++++++++++---------- SharedVersion.cs | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Emby.Drawing/ImageProcessor.cs b/Emby.Drawing/ImageProcessor.cs index a15f75c9a..5b04ceea2 100644 --- a/Emby.Drawing/ImageProcessor.cs +++ b/Emby.Drawing/ImageProcessor.cs @@ -238,7 +238,7 @@ namespace Emby.Drawing var outputFormat = GetOutputFormat(options.SupportedOutputFormats[0]); var cacheFilePath = GetCacheFilePath(originalImagePath, newSize, quality, dateModified, outputFormat, options.AddPlayedIndicator, options.PercentPlayed, options.UnplayedCount, options.Blur, options.BackgroundColor, options.ForegroundLayer); - var imageProcessingLockTaken = false; + //var imageProcessingLockTaken = false; try { @@ -253,9 +253,9 @@ namespace Emby.Drawing var tmpPath = Path.ChangeExtension(Path.Combine(_appPaths.TempDirectory, Guid.NewGuid().ToString("N")), Path.GetExtension(cacheFilePath)); _fileSystem.CreateDirectory(Path.GetDirectoryName(tmpPath)); - await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false); + //await _imageProcessingSemaphore.WaitAsync().ConfigureAwait(false); - imageProcessingLockTaken = true; + //imageProcessingLockTaken = true; _imageEncoder.EncodeImage(originalImagePath, tmpPath, AutoOrient(options.Item), newWidth, newHeight, quality, options, outputFormat); CopyFile(tmpPath, cacheFilePath); @@ -273,13 +273,13 @@ namespace Emby.Drawing // Just spit out the original file if all the options are default return new Tuple(originalImagePath, MimeTypes.GetMimeType(originalImagePath), dateModified); } - finally - { - if (imageProcessingLockTaken) - { - _imageProcessingSemaphore.Release(); - } - } + //finally + //{ + // if (imageProcessingLockTaken) + // { + // _imageProcessingSemaphore.Release(); + // } + //} } private void CopyFile(string src, string destination) diff --git a/SharedVersion.cs b/SharedVersion.cs index 3f2c5d97e..825a8b919 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.7.3")] +[assembly: AssemblyVersion("3.2.7.4")]