diff --git a/Emby.Dlna/PlayTo/Device.cs b/Emby.Dlna/PlayTo/Device.cs index b14f1657a5..f6be479898 100644 --- a/Emby.Dlna/PlayTo/Device.cs +++ b/Emby.Dlna/PlayTo/Device.cs @@ -20,9 +20,6 @@ namespace Emby.Dlna.PlayTo { public class Device : IDisposable { - const string ServiceAvtransportType = "urn:schemas-upnp-org:service:AVTransport:1"; - const string ServiceRenderingType = "urn:schemas-upnp-org:service:RenderingControl:1"; - #region Fields & Properties private ITimer _timer; @@ -254,13 +251,29 @@ namespace Emby.Dlna.PlayTo } } + private DeviceService GetServiceRenderingControl() + { + var services = Properties.Services; + + return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:RenderingControl:1", StringComparison.OrdinalIgnoreCase)) ?? + services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:RenderingControl", StringComparison.OrdinalIgnoreCase)); + } + + private DeviceService GetAvTransportService() + { + var services = Properties.Services; + + return services.FirstOrDefault(s => string.Equals(s.ServiceType, "urn:schemas-upnp-org:service:AVTransport:1", StringComparison.OrdinalIgnoreCase)) ?? + services.FirstOrDefault(s => (s.ServiceType ?? string.Empty).StartsWith("urn:schemas-upnp-org:service:AVTransport", StringComparison.OrdinalIgnoreCase)); + } + private async Task SetMute(bool mute) { var command = RendererCommands.ServiceActions.FirstOrDefault(c => c.Name == "SetMute"); if (command == null) return false; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType); + var service = GetServiceRenderingControl(); if (service == null) { @@ -287,7 +300,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType); + var service = GetServiceRenderingControl(); if (service == null) { @@ -308,7 +321,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) { @@ -333,7 +346,7 @@ namespace Emby.Dlna.PlayTo {"CurrentURIMetaData", CreateDidlMeta(metaData)} }; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) { @@ -373,7 +386,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) { @@ -390,7 +403,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1)) .ConfigureAwait(false); @@ -402,7 +415,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.First(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); await new SsdpHttpClient(_httpClient, _config).SendCommandAsync(Properties.BaseUrl, service, command.Name, AvCommands.BuildPost(command, service.ServiceType, 1)) .ConfigureAwait(false); @@ -521,7 +534,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType); + var service = GetServiceRenderingControl(); if (service == null) { @@ -554,7 +567,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType); + var service = GetServiceRenderingControl(); if (service == null) { @@ -579,7 +592,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return null; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) return null; @@ -613,7 +626,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return null; - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) { @@ -644,7 +657,7 @@ namespace Emby.Dlna.PlayTo if (command == null) return new Tuple(false, null); - var service = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var service = GetAvTransportService(); if (service == null) { @@ -697,7 +710,7 @@ namespace Emby.Dlna.PlayTo } XElement uPnpResponse; - + // Handle different variations sent back by devices try { @@ -780,28 +793,28 @@ namespace Emby.Dlna.PlayTo private async Task GetAVProtocolAsync() { - var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceAvtransportType); + var avService = GetAvTransportService(); if (avService == null) return; string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(url); + var document = await httpClient.GetDataAsync(url).ConfigureAwait(false); AvCommands = TransportCommands.Create(document); } private async Task GetRenderingProtocolAsync() { - var avService = Properties.Services.FirstOrDefault(s => s.ServiceType == ServiceRenderingType); + var avService = GetServiceRenderingControl(); if (avService == null) return; string url = NormalizeUrl(Properties.BaseUrl, avService.ScpdUrl); var httpClient = new SsdpHttpClient(_httpClient, _config); - var document = await httpClient.GetDataAsync(url); + var document = await httpClient.GetDataAsync(url).ConfigureAwait(false); RendererCommands = TransportCommands.Create(document); } @@ -899,8 +912,6 @@ namespace Emby.Dlna.PlayTo deviceProperties.Icon = CreateIcon(icon); } - var isRenderer = false; - foreach (var services in document.Descendants(uPnpNamespaces.ud.GetName("serviceList"))) { if (services == null) @@ -918,17 +929,13 @@ namespace Emby.Dlna.PlayTo if (service != null) { deviceProperties.Services.Add(service); - if (service.ServiceType == ServiceAvtransportType) - { - isRenderer = true; - } } } } var device = new Device(deviceProperties, httpClient, logger, config, timerFactory); - if (isRenderer) + if (device.GetAvTransportService() != null) { await device.GetRenderingProtocolAsync().ConfigureAwait(false); await device.GetAVProtocolAsync().ConfigureAwait(false); diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 5f2a314f9f..1517029050 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -389,6 +389,8 @@ namespace Emby.Server.Implementations.Data }); } } + + GC.Collect(); } catch (Exception ex) { diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index 1ff61286f9..5bf53fcb43 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -855,7 +855,7 @@ namespace Emby.Server.Implementations.Library SortOrder = SortOrder.Descending, Limit = 1 }; - + return GetItemList(query) .FirstOrDefault(); } @@ -1113,16 +1113,21 @@ namespace Emby.Server.Implementations.Library progress.Report(1); - var userRoot = GetUserRootFolder(); + await GetUserRootFolder().RefreshMetadata(cancellationToken).ConfigureAwait(false); - await userRoot.RefreshMetadata(cancellationToken).ConfigureAwait(false); - - await userRoot.ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); + await GetUserRootFolder().ValidateChildren(new Progress(), cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: false).ConfigureAwait(false); progress.Report(2); + // Quickly scan CollectionFolders for changes + foreach (var folder in GetUserRootFolder().Children.OfType().ToList()) + { + await folder.RefreshMetadata(cancellationToken).ConfigureAwait(false); + } + progress.Report(3); + var innerProgress = new ActionableProgress(); - innerProgress.RegisterAction(pct => progress.Report(2 + pct * .73)); + innerProgress.RegisterAction(pct => progress.Report(3 + pct * .72)); // Now validate the entire media library await RootFolder.ValidateChildren(innerProgress, cancellationToken, new MetadataRefreshOptions(_fileSystem), recursive: true).ConfigureAwait(false); @@ -1291,7 +1296,6 @@ namespace Emby.Server.Implementations.Library if (parent != null) { SetTopParentIdsOrAncestors(query, new List { parent }); - query.ParentId = null; } } @@ -1311,7 +1315,6 @@ namespace Emby.Server.Implementations.Library if (parent != null) { SetTopParentIdsOrAncestors(query, new List { parent }); - query.ParentId = null; } } @@ -1456,6 +1459,12 @@ namespace Emby.Server.Implementations.Library // Optimize by querying against top level views query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray(); query.AncestorIds = new string[] { }; + + // Prevent searching in all libraries due to empty filter + if (query.TopParentIds.Length == 0) + { + query.TopParentIds = new[] { Guid.NewGuid().ToString("N") }; + } } } @@ -1478,7 +1487,6 @@ namespace Emby.Server.Implementations.Library if (parent != null) { SetTopParentIdsOrAncestors(query, new List { parent }); - query.ParentId = null; } } @@ -1514,12 +1522,26 @@ namespace Emby.Server.Implementations.Library { // Optimize by querying against top level views query.TopParentIds = parents.SelectMany(i => GetTopParentIdsForQuery(i, query.User)).Select(i => i.ToString("N")).ToArray(); + + // Prevent searching in all libraries due to empty filter + if (query.TopParentIds.Length == 0) + { + query.TopParentIds = new[] { Guid.NewGuid().ToString("N") }; + } } else { // We need to be able to query from any arbitrary ancestor up the tree query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray(); + + // Prevent searching in all libraries due to empty filter + if (query.AncestorIds.Length == 0) + { + query.AncestorIds = new[] { Guid.NewGuid().ToString("N") }; + } } + + query.ParentId = null; } private void AddUserToQuery(InternalItemsQuery query, User user) @@ -1561,7 +1583,7 @@ namespace Emby.Server.Implementations.Library }, CancellationToken.None).Result; - return channelResult.Items.Select(i => i.Id); + return channelResult.Items.Select(i => i.Id); } // Translate view into folders @@ -1602,7 +1624,7 @@ namespace Emby.Server.Implementations.Library { return collectionFolder.PhysicalFolderIds; } - + var topParent = item.GetTopParent(); if (topParent != null) { diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 5e6de1d4db..6bf4125253 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -144,23 +144,16 @@ namespace Emby.Server.Implementations.TV // If viewing all next up for all series, remove first episodes // But if that returns empty, keep those first episodes (avoid completely empty view) var alwaysEnableFirstEpisode = !string.IsNullOrWhiteSpace(request.SeriesId); - var isFirstItemAFirstEpisode = true; return allNextUp .Where(i => { if (alwaysEnableFirstEpisode || i.Item1 != DateTime.MinValue) { - isFirstItemAFirstEpisode = false; return true; } - if (isFirstItemAFirstEpisode) - { - return false; - } - - return true; + return false; }) .Select(i => i.Item2()) .Where(i => i != null) diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 07ec6d955a..1f21a1dd15 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -145,7 +145,8 @@ namespace MediaBrowser.Api client.IndexOf("media center", StringComparison.OrdinalIgnoreCase) != -1 || client.IndexOf("classic", StringComparison.OrdinalIgnoreCase) != -1 || client.IndexOf("roku", StringComparison.OrdinalIgnoreCase) != -1 || - client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1) + client.IndexOf("samsung", StringComparison.OrdinalIgnoreCase) != -1 || + client.IndexOf("androidtv", StringComparison.OrdinalIgnoreCase) != -1) { options.Fields.Add(Model.Querying.ItemFields.ChildCount); } diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index c505aefb3f..681f16f07c 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -201,6 +201,11 @@ namespace MediaBrowser.Controller.Entities } protected override bool RefreshLinkedChildren(IEnumerable fileSystemChildren) + { + return RefreshLinkedChildrenInternal(true); + } + + private bool RefreshLinkedChildrenInternal(bool setFolders) { var physicalFolders = GetPhysicalFolders(false) .ToList(); @@ -219,7 +224,10 @@ namespace MediaBrowser.Controller.Entities if (!folderIds.SequenceEqual(newFolderIds)) { changed = true; - PhysicalFolderIds = newFolderIds.ToList(); + if (setFolders) + { + PhysicalFolderIds = newFolderIds.ToList(); + } } return changed;