diff options
Diffstat (limited to 'MediaBrowser.Server.Implementations')
37 files changed, 1098 insertions, 1054 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index b115c3bfd..fe13d8cef 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -104,6 +104,11 @@ namespace MediaBrowser.Server.Implementations.Channels .OrderBy(i => i.Name); } + public IEnumerable<Guid> GetInstalledChannelIds() + { + return GetAllChannels().Select(i => GetInternalChannelId(i.Name)); + } + public Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken) { var user = string.IsNullOrWhiteSpace(query.UserId) @@ -408,25 +413,15 @@ namespace MediaBrowser.Server.Implementations.Channels private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken) { + var parentFolder = await GetInternalChannelFolder(cancellationToken).ConfigureAwait(false); + var parentFolderId = parentFolder.Id; + var id = GetInternalChannelId(channelInfo.Name); var path = Channel.GetInternalMetadataPath(_config.ApplicationPaths.InternalMetadataPath, id); var isNew = false; - - if (!_fileSystem.DirectoryExists(path)) - { - _logger.Debug("Creating directory {0}", path); - - _fileSystem.CreateDirectory(path); - - if (!_fileSystem.DirectoryExists(path)) - { - throw new IOException("Path not created: " + path); - } - - isNew = true; - } + var forceUpdate = false; var item = _libraryManager.GetItemById(id) as Channel; var channelId = channelInfo.Name.GetMD5().ToString("N"); @@ -438,18 +433,29 @@ namespace MediaBrowser.Server.Implementations.Channels Name = channelInfo.Name, Id = id, DateCreated = _fileSystem.GetCreationTimeUtc(path), - DateModified = _fileSystem.GetLastWriteTimeUtc(path), - Path = path, - ChannelId = channelId + DateModified = _fileSystem.GetLastWriteTimeUtc(path) }; isNew = true; } - if (!string.Equals(item.ChannelId, channelId, StringComparison.OrdinalIgnoreCase)) + if (!string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { isNew = true; } + item.Path = path; + + if (!string.Equals(item.ChannelId, channelId, StringComparison.OrdinalIgnoreCase)) + { + forceUpdate = true; + } + item.ChannelId = channelId; + + if (item.ParentId != parentFolderId) + { + forceUpdate = true; + } + item.ParentId = parentFolderId; item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating); item.Overview = channelInfo.Description; @@ -459,13 +465,17 @@ namespace MediaBrowser.Server.Implementations.Channels { item.Name = channelInfo.Name; } - - await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem) - { - ForceSave = isNew - }, cancellationToken); + if (isNew) + { + await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); + } + else if (forceUpdate) + { + await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false); + } + await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken); return item; } @@ -1225,6 +1235,7 @@ namespace MediaBrowser.Server.Implementations.Channels { BaseItem item; bool isNew; + bool forceUpdate = false; if (info.Type == ChannelItemType.Folder) { @@ -1254,24 +1265,25 @@ namespace MediaBrowser.Server.Implementations.Channels item.ProductionYear = info.ProductionYear; item.ProviderIds = info.ProviderIds; item.OfficialRating = info.OfficialRating; - item.DateCreated = info.DateCreated ?? DateTime.UtcNow; + item.Tags = info.Tags; } var channelItem = (IChannelItem)item; channelItem.ChannelId = internalChannelId.ToString("N"); - if (!string.Equals(channelItem.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase)) + if (item.ParentId != internalChannelId) { - isNew = true; + forceUpdate = true; } - channelItem.ExternalId = info.Id; + item.ParentId = internalChannelId; - if (isNew) + if (!string.Equals(channelItem.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase)) { - channelItem.Tags = info.Tags; + forceUpdate = true; } + channelItem.ExternalId = info.Id; var channelMediaItem = item as IChannelMediaItem; @@ -1300,6 +1312,10 @@ namespace MediaBrowser.Server.Implementations.Channels await _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>()).ConfigureAwait(false); } } + else if (forceUpdate) + { + await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false); + } return item; } @@ -1573,4 +1589,4 @@ namespace MediaBrowser.Server.Implementations.Channels } } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs index 2e9d42f49..da4a72cd4 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -123,15 +123,15 @@ namespace MediaBrowser.Server.Implementations.Channels private async Task CleanDatabase(CancellationToken cancellationToken) { - var allChannels = await _channelManager.GetChannelsInternal(new ChannelQuery { }, cancellationToken); + var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds(); - var allIds = _libraryManager.GetItemIds(new InternalItemsQuery + var databaseIds = _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Channel).Name } }); - var invalidIds = allIds - .Except(allChannels.Items.Select(i => i.Id).ToList()) + var invalidIds = databaseIds + .Except(installedChannelIds) .ToList(); foreach (var id in invalidIds) diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs b/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs index 9ea457284..7ed0d43b1 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.Collections return subItem; } - var parent = subItem.Parent; + var parent = subItem.GetParent(); if (parent != null && parent.HasImage(ImageType.Primary)) { @@ -78,24 +78,9 @@ namespace MediaBrowser.Server.Implementations.Collections return Task.FromResult(GetFinalItems(items, 2)); } - protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) + protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex) { - var image = itemsWithImages - .Where(i => i.HasImage(ImageType.Primary) && i.GetImageInfo(ImageType.Primary, 0).IsLocalFile && Path.HasExtension(i.GetImagePath(ImageType.Primary))) - .Select(i => i.GetImagePath(ImageType.Primary)) - .FirstOrDefault(); - - if (string.IsNullOrWhiteSpace(image)) - { - return null; - } - - var ext = Path.GetExtension(image); - - var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ext); - File.Copy(image, outputPath); - - return outputPath; + return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary); } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index 5c98e401f..4e742ca7a 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -40,6 +40,7 @@ namespace MediaBrowser.Server.Implementations.Collections public Folder GetCollectionsFolder(string userId) { return _libraryManager.RootFolder.Children.OfType<ManualCollectionsFolder>() + .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<ManualCollectionsFolder>() .FirstOrDefault(); } diff --git a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs index d62918d56..7a1d86047 100644 --- a/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs +++ b/MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs @@ -3,7 +3,7 @@ using System.Linq; namespace MediaBrowser.Server.Implementations.Collections { - public class ManualCollectionsFolder : BasePluginFolder + public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay { public ManualCollectionsFolder() { @@ -11,11 +11,6 @@ namespace MediaBrowser.Server.Implementations.Collections DisplayMediaType = "CollectionFolder"; } - public override bool IsVisible(User user) - { - return base.IsVisible(user) && GetChildren(user, false).Any(); - } - public override bool IsHidden { get @@ -24,7 +19,7 @@ namespace MediaBrowser.Server.Implementations.Collections } } - public override bool IsHiddenFromUser(User user) + public bool IsHiddenFromUser(User user) { return !user.Configuration.DisplayCollectionsView; } @@ -36,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Collections public override string GetClientTypeName() { - return typeof (CollectionFolder).Name; + return typeof(CollectionFolder).Name; } } }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs index 6d7265972..947933561 100644 --- a/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs +++ b/MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs @@ -3,12 +3,15 @@ using MediaBrowser.Controller.Entities; using System; using System.IO; using System.Linq; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; using CommonIO; -using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Providers; namespace MediaBrowser.Server.Implementations.Devices { - public class CameraUploadsFolder : BasePluginFolder + public class CameraUploadsFolder : BasePluginFolder, ISupportsUserSpecificView { public CameraUploadsFolder() { @@ -21,32 +24,41 @@ namespace MediaBrowser.Server.Implementations.Devices { return false; } - - return GetChildren(user, true).Any() && - base.IsVisible(user); + + return base.IsVisible(user) && HasChildren(); } - public override bool IsHidden + public override string CollectionType { - get - { - return base.IsHidden || !Children.Any(); - } + get { return Model.Entities.CollectionType.Photos; } } - public override bool IsHiddenFromUser(User user) + public override string GetClientTypeName() { - return false; + return typeof(CollectionFolder).Name; } - public override string CollectionType + private bool? _hasChildren; + private bool HasChildren() { - get { return Model.Entities.CollectionType.Photos; } + if (!_hasChildren.HasValue) + { + _hasChildren = LibraryManager.GetItemIds(new InternalItemsQuery { ParentId = Id }).Count > 0; + } + + return _hasChildren.Value; } - public override string GetClientTypeName() + protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService) { - return typeof(CollectionFolder).Name; + _hasChildren = null; + return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService); + } + + [IgnoreDataMember] + public bool EnableUserSpecificView + { + get { return true; } } } @@ -65,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Devices { var path = Path.Combine(_appPaths.DataPath, "camerauploads"); - _fileSystem.CreateDirectory(path); + _fileSystem.CreateDirectory(path); return new CameraUploadsFolder { diff --git a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs index 0b2c082a8..6b1af8d2d 100644 --- a/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs +++ b/MediaBrowser.Server.Implementations/Devices/DeviceManager.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Events; -using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Devices; using MediaBrowser.Controller.Library; @@ -18,6 +17,7 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Configuration; namespace MediaBrowser.Server.Implementations.Devices { @@ -27,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.Devices private readonly IUserManager _userManager; private readonly IFileSystem _fileSystem; private readonly ILibraryMonitor _libraryMonitor; - private readonly IConfigurationManager _config; + private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly INetworkManager _network; @@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Devices /// </summary> public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated; - public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config, ILogger logger, INetworkManager network) + public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network) { _repo = repo; _userManager = userManager; @@ -187,11 +187,6 @@ namespace MediaBrowser.Server.Implementations.Devices } } - private string GetUploadPath(string deviceId) - { - return GetUploadPath(GetDevice(deviceId)); - } - private string GetUploadPath(DeviceInfo device) { if (!string.IsNullOrWhiteSpace(device.CameraUploadPath)) @@ -205,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Devices return config.CameraUploadPath; } - var path = Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); + var path = DefaultCameraUploadsPath; if (config.EnableCameraUploadSubfolders) { @@ -215,6 +210,11 @@ namespace MediaBrowser.Server.Implementations.Devices return path; } + private string DefaultCameraUploadsPath + { + get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); } + } + public async Task UpdateDeviceInfo(string id, DeviceOptions options) { var device = GetDevice(id); diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs index bb7f818ba..ccca6414a 100644 --- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs +++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs @@ -163,16 +163,11 @@ namespace MediaBrowser.Server.Implementations.Dto if (person != null) { - var items = _libraryManager.GetItems(new InternalItemsQuery + var items = _libraryManager.GetItems(new InternalItemsQuery(user) { Person = byName.Name - }).Items; - - if (user != null) - { - return items.Where(i => i.IsVisibleStandalone(user)).ToList(); - } + }, new string[] { }); return items.ToList(); } @@ -361,6 +356,8 @@ namespace MediaBrowser.Server.Implementations.Dto var collectionFolder = item as ICollectionFolder; if (collectionFolder != null) { + dto.OriginalCollectionType = collectionFolder.CollectionType; + dto.CollectionType = user == null ? collectionFolder.CollectionType : collectionFolder.GetViewType(user); @@ -468,13 +465,15 @@ namespace MediaBrowser.Server.Implementations.Dto var folder = (Folder)item; - dto.ChildCount = GetChildCount(folder, user); - - // These are just far too slow. - // TODO: Disable for CollectionFolder - if (!(folder is UserRootFolder) && !(folder is UserView)) + if (!(folder is IChannelItem) && !(folder is Channel)) { - SetSpecialCounts(folder, user, dto, fields, syncProgress); + dto.ChildCount = GetChildCount(folder, user); + + // These are just far too slow. + if (!(folder is UserRootFolder) && !(folder is UserView) && !(folder is ICollectionFolder)) + { + SetSpecialCounts(folder, user, dto, fields, syncProgress); + } } dto.UserData.Played = dto.UserData.PlayedPercentage.HasValue && dto.UserData.PlayedPercentage.Value >= 100; @@ -815,7 +814,7 @@ namespace MediaBrowser.Server.Implementations.Dto /// <returns>BaseItem.</returns> private BaseItem GetParentBackdropItem(BaseItem item, BaseItem owner) { - var parent = item.Parent ?? owner; + var parent = item.GetParent() ?? owner; while (parent != null) { @@ -824,7 +823,7 @@ namespace MediaBrowser.Server.Implementations.Dto return parent; } - parent = parent.Parent; + parent = parent.GetParent(); } return null; @@ -839,7 +838,7 @@ namespace MediaBrowser.Server.Implementations.Dto /// <returns>BaseItem.</returns> private BaseItem GetParentImageItem(BaseItem item, ImageType type, BaseItem owner) { - var parent = item.Parent ?? owner; + var parent = item.GetParent() ?? owner; while (parent != null) { @@ -848,7 +847,7 @@ namespace MediaBrowser.Server.Implementations.Dto return parent; } - parent = parent.Parent; + parent = parent.GetParent(); } return null; @@ -1042,7 +1041,11 @@ namespace MediaBrowser.Server.Implementations.Dto dto.IsFolder = item.IsFolder; dto.MediaType = item.MediaType; dto.LocationType = item.LocationType; - dto.IsHD = item.IsHD; + if (item.IsHD.HasValue && item.IsHD.Value) + { + dto.IsHD = item.IsHD; + } + dto.Audio = item.Audio; dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode; dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage; @@ -1209,15 +1212,15 @@ namespace MediaBrowser.Server.Implementations.Dto dto.VoteCount = item.VoteCount; } - if (item.IsFolder) - { - var folder = (Folder)item; + //if (item.IsFolder) + //{ + // var folder = (Folder)item; - if (fields.Contains(ItemFields.IndexOptions)) - { - dto.IndexOptions = folder.IndexByOptionStrings.ToArray(); - } - } + // if (fields.Contains(ItemFields.IndexOptions)) + // { + // dto.IndexOptions = folder.IndexByOptionStrings.ToArray(); + // } + //} var supportsPlaceHolders = item as ISupportsPlaceHolders; if (supportsPlaceHolders != null) @@ -1520,7 +1523,7 @@ namespace MediaBrowser.Server.Implementations.Dto } dto.ChannelId = item.ChannelId; - + var channelItem = item as IChannelItem; if (channelItem != null) { diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs index 008363ca4..b059e4144 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs @@ -74,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints // Go up one level for indicators if (baseItem != null) { - var parent = baseItem.Parent; + var parent = baseItem.GetParent(); if (parent != null) { diff --git a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs index e107ea9f1..edc6b14ab 100644 --- a/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs +++ b/MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs @@ -217,7 +217,7 @@ namespace MediaBrowser.Server.Implementations.IO /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e) { - if (e.Item.Parent is AggregateFolder) + if (e.Item.GetParent() is AggregateFolder) { StopWatchingPath(e.Item.Path); } @@ -230,7 +230,7 @@ namespace MediaBrowser.Server.Implementations.IO /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param> void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e) { - if (e.Item.Parent is AggregateFolder) + if (e.Item.GetParent() is AggregateFolder) { StartWatchingPath(e.Item.Path); } @@ -532,9 +532,16 @@ namespace MediaBrowser.Server.Implementations.IO return false; } + // In order to determine if the file is being written to, we have to request write access + // But if the server only has readonly access, this is going to cause this entire algorithm to fail + // So we'll take a best guess about our access level + var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta + ? FileAccess.ReadWrite + : FileAccess.Read; + try { - using (_fileSystem.GetFileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite)) + using (_fileSystem.GetFileStream(path, FileMode.Open, requestedFileAccess, FileShare.ReadWrite)) { if (_updateTimer != null) { @@ -651,7 +658,7 @@ namespace MediaBrowser.Server.Implementations.IO // If the item has been deleted find the first valid parent that still exists while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path)) { - item = item.Parent; + item = item.GetParent(); if (item == null) { diff --git a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs index ec94e16db..edc329ec4 100644 --- a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs +++ b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -41,11 +41,6 @@ namespace MediaBrowser.Server.Implementations.Intros public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user) { - if (!user.Configuration.EnableCinemaMode) - { - return new List<IntroInfo>(); - } - var config = GetOptions(); if (item is Movie) @@ -83,13 +78,11 @@ namespace MediaBrowser.Server.Implementations.Intros if (config.EnableIntrosFromMoviesInLibrary) { - var inputItems = _libraryManager.GetItems(new InternalItemsQuery + var inputItems = _libraryManager.GetItems(new InternalItemsQuery(user) { - IncludeItemTypes = new[] { typeof(Movie).Name }, + IncludeItemTypes = new[] { typeof(Movie).Name } - User = user - - }).Items; + }, new string[]{}); var itemsWithTrailers = inputItems .Where(i => @@ -168,7 +161,7 @@ namespace MediaBrowser.Server.Implementations.Intros private IEnumerable<IntroInfo> GetResult(BaseItem item, IEnumerable<ItemWithTrailer> candidates, CinemaModeConfiguration config, int? ratingLevel) { var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ? - GetCustomIntros(item) : + GetCustomIntros(config) : new List<IntroInfo>(); var trailerLimit = config.TrailerLimit; @@ -217,11 +210,11 @@ namespace MediaBrowser.Server.Implementations.Intros return _serverConfig.GetConfiguration<CinemaModeConfiguration>("cinemamode"); } - private List<IntroInfo> GetCustomIntros(BaseItem item) + private List<IntroInfo> GetCustomIntros(CinemaModeConfiguration options) { try { - return GetCustomIntroFiles() + return GetCustomIntroFiles(options, true, false) .OrderBy(i => Guid.NewGuid()) .Select(i => new IntroInfo { @@ -235,17 +228,23 @@ namespace MediaBrowser.Server.Implementations.Intros } } - private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options = null) + private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros) { - options = options ?? GetOptions(); + var list = new List<string>(); + + if (enableCustomIntros && !string.IsNullOrWhiteSpace(options.CustomIntroPath)) + { + list.AddRange(_fileSystem.GetFilePaths(options.CustomIntroPath, true) + .Where(_libraryManager.IsVideoFile)); + } - if (string.IsNullOrWhiteSpace(options.CustomIntroPath)) + if (enableMediaInfoIntros && !string.IsNullOrWhiteSpace(options.MediaInfoIntroPath)) { - return new List<string>(); + list.AddRange(_fileSystem.GetFilePaths(options.MediaInfoIntroPath, true) + .Where(_libraryManager.IsVideoFile)); } - return _fileSystem.GetFilePaths(options.CustomIntroPath, true) - .Where(_libraryManager.IsVideoFile); + return list.Distinct(StringComparer.OrdinalIgnoreCase); } private bool FilterByParentalRating(int? ratingLevel, BaseItem item) @@ -346,7 +345,7 @@ namespace MediaBrowser.Server.Implementations.Intros public IEnumerable<string> GetAllIntroFiles() { - return GetCustomIntroFiles(); + return GetCustomIntroFiles(GetOptions(), true, true); } private bool IsSupporter diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 9035d6479..402fa439d 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -47,11 +47,14 @@ namespace MediaBrowser.Server.Implementations.Library /// <summary> /// Shoulds the ignore. /// </summary> - /// <param name="args">The args.</param> + /// <param name="fileInfo">The file information.</param> + /// <param name="parent">The parent.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public bool ShouldIgnore(ItemResolveArgs args) + public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent) { - var filename = args.FileInfo.Name; + var filename = fileInfo.Name; + var isHidden = (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden; + var path = fileInfo.FullName; // Handle mac .DS_Store // https://github.com/MediaBrowser/MediaBrowser/issues/427 @@ -61,21 +64,24 @@ namespace MediaBrowser.Server.Implementations.Library } // Ignore hidden files and folders - if (args.IsHidden) + if (isHidden) { - var parentFolderName = Path.GetFileName(Path.GetDirectoryName(args.Path)); - - if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - { - return false; - } - if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + if (parent == null) { - return false; + var parentFolderName = Path.GetFileName(Path.GetDirectoryName(path)); + + if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } + if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + { + return false; + } } // Sometimes these are marked hidden - if (_fileSystem.IsRootPath(args.Path)) + if (_fileSystem.IsRootPath(path)) { return false; } @@ -83,7 +89,7 @@ namespace MediaBrowser.Server.Implementations.Library return true; } - if (args.IsDirectory) + if (fileInfo.IsDirectory) { // Ignore any folders in our list if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase)) @@ -91,26 +97,29 @@ namespace MediaBrowser.Server.Implementations.Library return true; } - // Ignore trailer folders but allow it at the collection level - if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && - !(args.Parent is AggregateFolder) && !(args.Parent is UserRootFolder)) + if (parent != null) { - return true; - } + // Ignore trailer folders but allow it at the collection level + if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) && + !(parent is AggregateFolder) && !(parent is UserRootFolder)) + { + return true; + } - if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) - { - return true; - } + if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } - if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) - { - return true; + if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase)) + { + return true; + } } } else { - if (args.Parent != null) + if (parent != null) { // Don't resolve these into audio files if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename)) diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index f27f35c48..9e19ffbe0 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -27,6 +27,7 @@ using MediaBrowser.Server.Implementations.Library.Validators; using MediaBrowser.Server.Implementations.Logging; using MediaBrowser.Server.Implementations.ScheduledTasks; using System; +using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; @@ -36,6 +37,7 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Model.Extensions; +using MediaBrowser.Model.Library; using MoreLinq; using SortOrder = MediaBrowser.Model.Entities.SortOrder; @@ -140,6 +142,7 @@ namespace MediaBrowser.Server.Implementations.Library private readonly Func<ILibraryMonitor> _libraryMonitorFactory; private readonly Func<IProviderManager> _providerManagerFactory; + private readonly Func<IUserViewManager> _userviewManager; /// <summary> /// The _library items cache @@ -167,7 +170,7 @@ namespace MediaBrowser.Server.Implementations.Library /// <param name="userManager">The user manager.</param> /// <param name="configurationManager">The configuration manager.</param> /// <param name="userDataRepository">The user data repository.</param> - public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory) + public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager) { _logger = logger; _taskManager = taskManager; @@ -177,6 +180,7 @@ namespace MediaBrowser.Server.Implementations.Library _libraryMonitorFactory = libraryMonitorFactory; _fileSystem = fileSystem; _providerManagerFactory = providerManagerFactory; + _userviewManager = userviewManager; ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>(); _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>(); @@ -401,12 +405,12 @@ namespace MediaBrowser.Server.Implementations.Library { foreach (var path in item.GetDeletePaths().ToList()) { - if (_fileSystem.DirectoryExists(path)) + if (_fileSystem.DirectoryExists(path)) { _logger.Debug("Deleting path {0}", path); _fileSystem.DeleteDirectory(path, true); } - else if (_fileSystem.FileExists(path)) + else if (_fileSystem.FileExists(path)) { _logger.Debug("Deleting path {0}", path); _fileSystem.DeleteFile(path); @@ -580,7 +584,7 @@ namespace MediaBrowser.Server.Implementations.Library }; // Return null if ignore rules deem that we should do so - if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(args))) + if (IgnoreFile(args.FileInfo, args.Parent)) { return null; } @@ -616,6 +620,11 @@ namespace MediaBrowser.Server.Implementations.Library return ResolveItem(args); } + public bool IgnoreFile(FileSystemMetadata file, BaseItem parent) + { + return EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent)); + } + public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths) { var originalList = paths.ToList(); @@ -651,7 +660,7 @@ namespace MediaBrowser.Server.Implementations.Library public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, string collectionType) { - var fileList = files.ToList(); + var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList(); if (parent != null) { @@ -702,7 +711,7 @@ namespace MediaBrowser.Server.Implementations.Library { var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath; - _fileSystem.CreateDirectory(rootFolderPath); + _fileSystem.CreateDirectory(rootFolderPath); var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath)); @@ -732,6 +741,13 @@ namespace MediaBrowser.Server.Implementations.Library folder = dbItem; } + if (folder.ParentId != rootFolder.Id) + { + folder.ParentId = rootFolder.Id; + var task = folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None); + Task.WaitAll(task); + } + rootFolder.AddVirtualChild(folder); RegisterItem(folder); @@ -753,7 +769,7 @@ namespace MediaBrowser.Server.Implementations.Library { var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath; - _fileSystem.CreateDirectory(userRootPath); + _fileSystem.CreateDirectory(userRootPath); var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder; @@ -1005,9 +1021,9 @@ namespace MediaBrowser.Server.Implementations.Library private void SetPropertiesFromSongs(MusicArtist artist, IEnumerable<IHasMetadata> items) { - + } - + /// <summary> /// Validate and refresh the People sub-set of the IBN. /// The items are stored in the db but not loaded into memory until actually requested by an operation. @@ -1018,7 +1034,7 @@ namespace MediaBrowser.Server.Implementations.Library public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress) { // Ensure the location is available. - _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); + _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath); return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress); } @@ -1265,6 +1281,11 @@ namespace MediaBrowser.Server.Implementations.Library public QueryResult<BaseItem> GetItems(InternalItemsQuery query) { + if (query.User != null) + { + AddUserToQuery(query, query.User); + } + var result = ItemRepository.GetItemIdsList(query); var items = result.Select(GetItemById).Where(i => i != null).ToArray(); @@ -1277,14 +1298,140 @@ namespace MediaBrowser.Server.Implementations.Library public QueryResult<BaseItem> QueryItems(InternalItemsQuery query) { + if (query.User != null) + { + AddUserToQuery(query, query.User); + } + return ItemRepository.GetItems(query); } public List<Guid> GetItemIds(InternalItemsQuery query) { + if (query.User != null) + { + AddUserToQuery(query, query.User); + } + return ItemRepository.GetItemIdsList(query); } + public IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds) + { + var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList(); + + SetTopParentIdsOrAncestors(query, parents); + + return GetItemIds(query).Select(GetItemById); + } + + public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds) + { + var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList(); + + SetTopParentIdsOrAncestors(query, parents); + + return GetItems(query); + } + + private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents) + { + if (parents.All(i => + { + if ((i is ICollectionFolder) || (i is UserView)) + { + return true; + } + + _logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name); + return false; + + })) + { + // Optimize by querying against top level views + query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray(); + } + 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(); + } + } + + private void AddUserToQuery(InternalItemsQuery query, User user) + { + if (query.AncestorIds.Length == 0 && !query.ParentId.HasValue && query.ChannelIds.Length == 0 && query.TopParentIds.Length == 0) + { + var userViews = _userviewManager().GetUserViews(new UserViewQuery + { + UserId = user.Id.ToString("N"), + IncludeHidden = true + + }, CancellationToken.None).Result.ToList(); + + query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray(); + } + } + + private IEnumerable<BaseItem> GetTopParentsForQuery(BaseItem item, User user) + { + var view = item as UserView; + + if (view != null) + { + if (string.Equals(view.ViewType, CollectionType.LiveTv)) + { + return new[] { view }; + } + if (string.Equals(view.ViewType, CollectionType.Channels)) + { + // TODO: Return channels + return new[] { view }; + } + + // Translate view into folders + if (view.DisplayParentId != Guid.Empty) + { + var displayParent = GetItemById(view.DisplayParentId); + if (displayParent != null) + { + return GetTopParentsForQuery(displayParent, user); + } + return new BaseItem[] { }; + } + if (view.ParentId != Guid.Empty) + { + var displayParent = GetItemById(view.ParentId); + if (displayParent != null) + { + return GetTopParentsForQuery(displayParent, user); + } + return new BaseItem[] { }; + } + + // Handle grouping + if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType)) + { + var collectionFolders = user.RootFolder.GetChildren(user, true).OfType<CollectionFolder>().Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase)); + return collectionFolders.SelectMany(i => GetTopParentsForQuery(i, user)); + } + return new BaseItem[] { }; + } + + var collectionFolder = item as CollectionFolder; + if (collectionFolder != null) + { + return collectionFolder.GetPhysicalParents(); + } + + var topParent = item.GetTopParent(); + if (topParent != null) + { + return new[] { topParent }; + } + return new BaseItem[] { }; + } + /// <summary> /// Gets the intros. /// </summary> @@ -1577,9 +1724,9 @@ namespace MediaBrowser.Server.Implementations.Library public IEnumerable<Folder> GetCollectionFolders(BaseItem item) { - while (!(item.Parent is AggregateFolder) && item.Parent != null) + while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) { - item = item.Parent; + item = item.GetParent(); } if (item == null) @@ -1616,7 +1763,7 @@ namespace MediaBrowser.Server.Implementations.Library return type; } - return item.Parents + return item.GetParents() .Select(GetConfiguredContentType) .LastOrDefault(i => !string.IsNullOrWhiteSpace(i)); } @@ -1653,14 +1800,14 @@ namespace MediaBrowser.Server.Implementations.Library private string GetTopFolderContentType(BaseItem item) { - while (!(item.Parent is AggregateFolder) && item.Parent != null) + if (item == null) { - item = item.Parent; + return null; } - if (item == null) + while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null) { - return null; + item = item.GetParent(); } return GetUserRootFolder().Children @@ -1679,7 +1826,7 @@ namespace MediaBrowser.Server.Implementations.Library string sortName, CancellationToken cancellationToken) { - return GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken); + return GetNamedView(user, name, null, viewType, sortName, cancellationToken); } public async Task<UserView> GetNamedView(string name, @@ -1697,10 +1844,9 @@ namespace MediaBrowser.Server.Implementations.Library var refresh = false; - if (item == null || - !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) + if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { - _fileSystem.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -1717,12 +1863,6 @@ namespace MediaBrowser.Server.Implementations.Library refresh = true; } - if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) - { - item.ViewType = viewType; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - if (!refresh) { refresh = (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval; @@ -1748,40 +1888,14 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - public Task<UserView> GetNamedView(User user, + public async Task<UserView> GetNamedView(User user, string name, string parentId, string viewType, string sortName, - string uniqueId, CancellationToken cancellationToken) { - if (string.IsNullOrWhiteSpace(parentId)) - { - throw new ArgumentNullException("parentId"); - } - - return GetNamedViewInternal(user, name, parentId, viewType, sortName, uniqueId, cancellationToken); - } - - private async Task<UserView> GetNamedViewInternal(User user, - string name, - string parentId, - string viewType, - string sortName, - string uniqueId, - CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentNullException("name"); - } - - var idValues = "37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty); - if (!string.IsNullOrWhiteSpace(uniqueId)) - { - idValues += uniqueId; - } + var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty); var id = GetNewItemId(idValues, typeof(UserView)); @@ -1793,7 +1907,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item == null) { - _fileSystem.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -1816,18 +1930,6 @@ namespace MediaBrowser.Server.Implementations.Library isNew = true; } - if (!item.UserId.HasValue) - { - item.UserId = user.Id; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - - if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) - { - item.ViewType = viewType; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - var refresh = isNew || (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval; if (!refresh && item.DisplayParentId != Guid.Empty) @@ -1851,7 +1953,6 @@ namespace MediaBrowser.Server.Implementations.Library public async Task<UserView> GetShadowView(BaseItem parent, string viewType, string sortName, - string uniqueId, CancellationToken cancellationToken) { if (parent == null) @@ -1862,11 +1963,7 @@ namespace MediaBrowser.Server.Implementations.Library var name = parent.Name; var parentId = parent.Id; - var idValues = "37_namedview_" + name + parentId + (viewType ?? string.Empty); - if (!string.IsNullOrWhiteSpace(uniqueId)) - { - idValues += uniqueId; - } + var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty); var id = GetNewItemId(idValues, typeof(UserView)); @@ -1897,12 +1994,6 @@ namespace MediaBrowser.Server.Implementations.Library isNew = true; } - if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase)) - { - item.ViewType = viewType; - await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false); - } - var refresh = isNew || (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval; if (!refresh && item.DisplayParentId != Guid.Empty) @@ -1922,7 +2013,7 @@ namespace MediaBrowser.Server.Implementations.Library return item; } - + public async Task<UserView> GetNamedView(string name, string parentId, string viewType, @@ -1951,7 +2042,7 @@ namespace MediaBrowser.Server.Implementations.Library if (item == null) { - _fileSystem.CreateDirectory(path); + _fileSystem.CreateDirectory(path); item = new UserView { @@ -2198,21 +2289,21 @@ namespace MediaBrowser.Server.Implementations.Library return ResolvePaths(files, directoryService, null, null) .OfType<Video>() .Select(video => - { - // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = GetItemById(video.Id) as Video; - - if (dbItem != null) { - video = dbItem; - } + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = GetItemById(video.Id) as Video; - video.ExtraType = ExtraType.Trailer; + if (dbItem != null) + { + video = dbItem; + } - return video; + video.ExtraType = ExtraType.Trailer; - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); + return video; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); } public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService) @@ -2384,7 +2475,7 @@ namespace MediaBrowser.Server.Implementations.Library return ItemRepository.UpdatePeople(item.Id, people); } - private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1,1); + private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1, 1); public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex) { _logger.Debug("ConvertImageToLocal item {0}", item.Id); @@ -2403,4 +2494,4 @@ namespace MediaBrowser.Server.Implementations.Library return item.GetImageInfo(image.Type, imageIndex); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs index 99096441e..2105f1a47 100644 --- a/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MediaSourceManager.cs @@ -105,11 +105,6 @@ namespace MediaBrowser.Server.Implementations.Library return GetMediaStreamsForItem(list); } - private int GetMaxAllowedBitrateForExternalSubtitleStream() - { - return 30000000; - } - private IEnumerable<MediaStream> GetMediaStreamsForItem(IEnumerable<MediaStream> streams) { var list = streams.ToList(); @@ -120,25 +115,9 @@ namespace MediaBrowser.Server.Implementations.Library if (subtitleStreams.Count > 0) { - var videoStream = list.FirstOrDefault(i => i.Type == MediaStreamType.Video); - - int maxAllowedBitrateForExternalSubtitleStream = GetMaxAllowedBitrateForExternalSubtitleStream(); - - var videoBitrate = videoStream == null ? maxAllowedBitrateForExternalSubtitleStream : videoStream.BitRate ?? maxAllowedBitrateForExternalSubtitleStream; - foreach (var subStream in subtitleStreams) { - var supportsExternalStream = StreamSupportsExternalStream(subStream); - - if (!subStream.IsExternal) - { - if (supportsExternalStream && videoBitrate >= maxAllowedBitrateForExternalSubtitleStream) - { - supportsExternalStream = false; - } - } - - subStream.SupportsExternalStream = supportsExternalStream; + subStream.SupportsExternalStream = StreamSupportsExternalStream(subStream); } } diff --git a/MediaBrowser.Server.Implementations/Library/MusicManager.cs b/MediaBrowser.Server.Implementations/Library/MusicManager.cs index aee101ef4..11a1f190a 100644 --- a/MediaBrowser.Server.Implementations/Library/MusicManager.cs +++ b/MediaBrowser.Server.Implementations/Library/MusicManager.cs @@ -80,15 +80,13 @@ namespace MediaBrowser.Server.Implementations.Library { var genreList = genres.ToList(); - var inputItems = _libraryManager.GetItems(new InternalItemsQuery + var inputItems = _libraryManager.GetItems(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(Audio).Name }, - Genres = genreList.ToArray(), + Genres = genreList.ToArray() - User = user - - }).Items; + }, new string[] { }); var genresDictionary = genreList.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs index 4efa1071d..83fdd3da2 100644 --- a/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs +++ b/MediaBrowser.Server.Implementations/Library/ResolverHelper.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library item.Id = libraryManager.GetNewItemId(item.Path, item.GetType()); item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || - item.Parents.Any(i => i.IsLocked); + item.GetParents().Any(i => i.IsLocked); // Make sure DateCreated and DateModified have values var fileInfo = directoryService.GetFile(item.Path); @@ -78,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.Library EnsureName(item, args.FileInfo); item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 || - item.Parents.Any(i => i.IsLocked); + item.GetParents().Any(i => i.IsLocked); // Make sure DateCreated and DateModified have values EnsureDates(fileSystem, item, args, true); diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 3252db505..5dd12f3cd 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -67,7 +67,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies string collectionType, IDirectoryService directoryService) { - if (IsInvalid(parent, collectionType, files)) + if (IsInvalid(parent, collectionType)) { return null; } @@ -95,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies return ResolveVideos<Video>(parent, files, directoryService, false); } - if (parent is Series || parent.Parents.OfType<Series>().Any()) + if (parent is Series || parent.GetParents().OfType<Series>().Any()) { return null; } @@ -185,7 +185,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies { var collectionType = args.GetCollectionType(); - if (IsInvalid(args.Parent, collectionType, args.FileSystemChildren)) + if (IsInvalid(args.Parent, collectionType)) { return null; } @@ -193,14 +193,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies // Find movies with their own folders if (args.IsDirectory) { + var files = args.FileSystemChildren + .Where(i => !LibraryManager.IgnoreFile(i, args.Parent)) + .ToList(); + if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase)) { - return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType); + return FindMovie<MusicVideo>(args.Path, args.Parent, files, args.DirectoryService, collectionType); } if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase)) { - return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType); + return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType); } if (string.IsNullOrEmpty(collectionType)) @@ -208,7 +212,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies // Owned items should just use the plain video type if (args.Parent == null) { - return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType); + return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType); } if (args.HasParent<Series>()) @@ -216,12 +220,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies return null; } - return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType); + return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType); } if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase)) { - return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType); + return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType); } return null; @@ -494,7 +498,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies }; } - private bool IsInvalid(Folder parent, string collectionType, IEnumerable<FileSystemMetadata> files) + private bool IsInvalid(Folder parent, string collectionType) { if (parent != null) { diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs index 1a873f01e..e62049821 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs @@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV // Not officially supported but in some cases we can handle it. if (season == null) { - season = parent.Parents.OfType<Season>().FirstOrDefault(); + season = parent.GetParents().OfType<Season>().FirstOrDefault(); } // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs index d6aff1192..aff982b0d 100644 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs @@ -156,19 +156,18 @@ namespace MediaBrowser.Server.Implementations.Library } AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name); - - var mediaItems = _libraryManager.GetItems(new InternalItemsQuery + + var mediaItems = _libraryManager.GetItems(new InternalItemsQuery(user) { NameContains = searchTerm, ExcludeItemTypes = excludeItemTypes.ToArray(), IncludeItemTypes = includeItemTypes.ToArray(), - MaxParentalRating = user == null ? null : user.Policy.MaxParentalRating, - Limit = (query.Limit.HasValue ? (int?)(query.Limit.Value * 3) : null), + Limit = query.Limit, - }).Items; + }, new string[] { }); // Add search hints based on item name - hints.AddRange(mediaItems.Where(i => IncludeInSearch(i) && IsVisible(i, user)).Select(item => + hints.AddRange(mediaItems.Where(IncludeInSearch).Select(item => { var index = GetIndex(item.Name, searchTerm, terms); @@ -184,25 +183,6 @@ namespace MediaBrowser.Server.Implementations.Library return Task.FromResult(returnValue); } - private bool IsVisible(BaseItem item, User user) - { - if (user == null) - { - return true; - } - - if (item is IItemByName) - { - var dual = item as IHasDualAccess; - if (dual == null || dual.IsAccessedByName) - { - return true; - } - } - - return item.IsVisibleStandalone(user); - } - private bool IncludeInSearch(BaseItem item) { var episode = item as Episode; diff --git a/MediaBrowser.Server.Implementations/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs index 3c29cf15d..3ef625db6 100644 --- a/MediaBrowser.Server.Implementations/Library/UserManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs @@ -707,8 +707,7 @@ namespace MediaBrowser.Server.Implementations.Library Id = Guid.NewGuid(), DateCreated = DateTime.UtcNow, DateModified = DateTime.UtcNow, - UsesIdForConfigurationPath = true, - EnableUserViews = true + UsesIdForConfigurationPath = true }; } diff --git a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs index c2938475c..aca13e088 100644 --- a/MediaBrowser.Server.Implementations/Library/UserViewManager.cs +++ b/MediaBrowser.Server.Implementations/Library/UserViewManager.cs @@ -16,6 +16,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Server.Implementations.Library { @@ -48,103 +49,63 @@ namespace MediaBrowser.Server.Implementations.Library .OfType<Folder>() .ToList(); - var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList(); + if (!query.IncludeHidden) + { + folders = folders.Where(i => + { + var hidden = i as IHiddenFromDisplay; + return hidden == null || !hidden.IsHiddenFromUser(user); + }).ToList(); + } - var standaloneFolders = folders - .Where(i => UserView.IsExcludedFromGrouping(i) || !user.IsFolderGrouped(i.Id)) - .ToList(); + var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList(); - var foldersWithViewTypes = folders - .Except(standaloneFolders) - .OfType<ICollectionFolder>() - .ToList(); + var groupedFolders = new List<ICollectionFolder>(); var list = new List<Folder>(); - var enableUserViews = _config.Configuration.EnableUserViews || user.EnableUserViews; - - if (enableUserViews) + foreach (var folder in folders) { - foreach (var folder in standaloneFolders) + var collectionFolder = folder as ICollectionFolder; + var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; + + if (UserView.IsUserSpecific(folder)) { - var collectionFolder = folder as ICollectionFolder; - var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; + list.Add(await _libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken).ConfigureAwait(false)); + continue; + } - if (UserView.IsUserSpecific(folder)) - { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - else if (plainFolderIds.Contains(folder.Id)) - { - list.Add(await GetUserView(folder, folderViewType, false, string.Empty, cancellationToken).ConfigureAwait(false)); - } - else if (!string.IsNullOrWhiteSpace(folderViewType)) - { - list.Add(await GetUserView(folder, folderViewType, true, string.Empty, cancellationToken).ConfigureAwait(false)); - } - else - { - list.Add(folder); - } + if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType)) + { + list.Add(folder); + continue; } - } - else - { - // TODO: Deprecate this whole block - foreach (var folder in standaloneFolders) + + if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id)) { - var collectionFolder = folder as ICollectionFolder; - var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType; - - if (UserView.IsUserSpecific(folder)) - { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - else if (plainFolderIds.Contains(folder.Id)) - { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - else if (!string.IsNullOrWhiteSpace(folderViewType)) - { - list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false)); - } - else - { - list.Add(folder); - } + groupedFolders.Add(collectionFolder); + continue; } - } - - var parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user))) - .ToList(); - - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, list, CollectionType.TvShows, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false)); - } - - parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user))) - .ToList(); - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, list, CollectionType.Music, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false)); + if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + list.Add(await GetUserView(folder, folderViewType, string.Empty, cancellationToken).ConfigureAwait(false)); + } + else + { + list.Add(folder); + } } - parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user))) - .ToList(); - - if (parents.Count > 0) + foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows }) { - list.Add(await GetUserView(parents, list, CollectionType.Movies, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false)); - } - - parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Games, StringComparison.OrdinalIgnoreCase)) - .ToList(); + var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType)) + .ToList(); - if (parents.Count > 0) - { - list.Add(await GetUserView(parents, list, CollectionType.Games, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false)); + if (parents.Count > 0) + { + list.Add(await GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken).ConfigureAwait(false)); + } } if (user.Configuration.DisplayFoldersView) @@ -189,6 +150,18 @@ namespace MediaBrowser.Server.Implementations.Library { var index = orders.IndexOf(i.Id.ToString("N")); + if (index == -1) + { + var view = i as UserView; + if (view != null) + { + if (view.DisplayParentId != Guid.Empty) + { + index = orders.IndexOf(view.DisplayParentId.ToString("N")); + } + } + } + return index == -1 ? int.MaxValue : index; }) .ThenBy(sorted.IndexOf) @@ -209,32 +182,25 @@ namespace MediaBrowser.Server.Implementations.Library return GetUserSubView(name, parentId, type, sortName, cancellationToken); } - private async Task<UserView> GetUserView(List<ICollectionFolder> parents, List<Folder> currentViews, string viewType, string sortName, User user, bool enableUserViews, CancellationToken cancellationToken) + private async Task<Folder> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken) { - if (parents.Count == 1 && parents.All(i => string.Equals((enableUserViews ? i.GetViewType(user) : i.CollectionType), viewType, StringComparison.OrdinalIgnoreCase))) + if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase))) { - var parentId = parents[0].Id; - - var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase); + if (!presetViews.Contains(viewType, StringComparer.OrdinalIgnoreCase)) + { + return (Folder)parents[0]; + } - return await GetUserView((Folder)parents[0], viewType, enableRichView, string.Empty, cancellationToken).ConfigureAwait(false); + return await GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken).ConfigureAwait(false); } var name = _localizationManager.GetLocalizedString("ViewType" + viewType); return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false); } - public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken) - { - viewType = enableRichView ? viewType : null; - return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, null, cancellationToken); - } - - public Task<UserView> GetUserView(Folder parent, string viewType, bool enableRichView, string sortName, CancellationToken cancellationToken) + public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken) { - viewType = enableRichView ? viewType : null; - - return _libraryManager.GetShadowView(parent, viewType, sortName, null, cancellationToken); + return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken); } public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request) @@ -245,16 +211,8 @@ namespace MediaBrowser.Server.Implementations.Library var currentUser = user; - Func<BaseItem, bool> filter = i => + var libraryItems = GetItemsForLatestItems(user, request.ParentId, includeTypes, request.Limit ?? 10).Where(i => { - if (includeTypes.Length > 0) - { - if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase)) - { - return false; - } - } - if (request.IsPlayed.HasValue) { var val = request.IsPlayed.Value; @@ -264,29 +222,12 @@ namespace MediaBrowser.Server.Implementations.Library } } - return i.LocationType != LocationType.Virtual && !i.IsFolder; - }; - - // Avoid implicitly captured closure - var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ? - GetItemsConfiguredForLatest(user, filter) : - GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, filter); - - libraryItems = libraryItems.OrderByDescending(i => i.DateCreated); - - if (request.IsPlayed.HasValue) - { - var takeLimit = (request.Limit ?? 20) * 20; - libraryItems = libraryItems.Take(takeLimit); - } - - // Avoid implicitly captured closure - var items = libraryItems - .ToList(); + return true; + }); var list = new List<Tuple<BaseItem, List<BaseItem>>>(); - foreach (var item in items) + foreach (var item in libraryItems) { // Only grab the index container for media var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer; @@ -318,59 +259,34 @@ namespace MediaBrowser.Server.Implementations.Library return list; } - protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter) + private IEnumerable<BaseItem> GetItemsForLatestItems(User user, string parentId, string[] includeItemTypes, int limit) { - if (!string.IsNullOrEmpty(parentId)) - { - var folder = (Folder)libraryManager.GetItemById(new Guid(parentId)); - - if (!string.IsNullOrWhiteSpace(userId)) - { - var user = userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return folder - .GetRecursiveChildren(user, filter) - .ToList(); - } + var parentIds = string.IsNullOrEmpty(parentId) + ? new string[] { } + : new[] { parentId }; - return folder - .GetRecursiveChildren(filter); - } - if (!string.IsNullOrWhiteSpace(userId)) + if (parentIds.Length == 0) { - var user = userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return user - .RootFolder - .GetRecursiveChildren(user, filter) - .ToList(); + parentIds = user.RootFolder.GetChildren(user, true) + .OfType<Folder>() + .Select(i => i.Id.ToString("N")) + .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i)) + .ToArray(); } - return libraryManager - .RootFolder - .GetRecursiveChildren(filter); - } - - private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem, bool> filter) - { - // Avoid implicitly captured closure - var currentUser = user; + var excludeItemTypes = includeItemTypes.Length == 0 ? new[] { "ChannelItem", "LiveTvItem", typeof(Person).Name, typeof(Studio).Name, typeof(Year).Name, typeof(GameGenre).Name, typeof(MusicGenre).Name, typeof(Genre).Name } : new string[] { }; - return user.RootFolder.GetChildren(user, true) - .OfType<Folder>() - .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N"))) - .SelectMany(i => i.GetRecursiveChildren(currentUser, filter)) - .DistinctBy(i => i.Id); + return _libraryManager.GetItems(new InternalItemsQuery(user) + { + IncludeItemTypes = includeItemTypes, + SortOrder = SortOrder.Descending, + SortBy = new[] { ItemSortBy.DateCreated }, + IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null, + ExcludeItemTypes = excludeItemTypes, + ExcludeLocationTypes = new[] { LocationType.Virtual }, + Limit = limit * 20 + + }, parentIds); } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListings.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListings.cs deleted file mode 100644 index ae441b44e..000000000 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListings.cs +++ /dev/null @@ -1,59 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.LiveTv; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Serialization; - -namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby -{ - public class EmbyGuide : IListingsProvider - { - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _jsonSerializer; - - public EmbyGuide(IHttpClient httpClient, IJsonSerializer jsonSerializer) - { - _httpClient = httpClient; - _jsonSerializer = jsonSerializer; - } - - public string Name - { - get { return "Emby Guide"; } - } - - public string Type - { - get { return "emby"; } - } - - public Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, string channelName, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) - { - return GetListingsProvider(info.Country).GetProgramsAsync(info, channelNumber, startDateUtc, endDateUtc, cancellationToken); - } - - public Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken) - { - return GetListingsProvider(info.Country).AddMetadata(info, channels, cancellationToken); - } - - public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) - { - return GetListingsProvider(info.Country).Validate(info, validateLogin, validateListings); - } - - public Task<List<NameIdPair>> GetLineups(ListingsProviderInfo info, string country, string location) - { - return GetListingsProvider(country).GetLineups(country, location); - } - - private IEmbyListingProvider GetListingsProvider(string country) - { - return new EmbyListingsNorthAmerica(_httpClient, _jsonSerializer); - } - } -} diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs deleted file mode 100644 index 2993740d6..000000000 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/EmbyListingsNorthAmerica.cs +++ /dev/null @@ -1,366 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.LiveTv; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby -{ - public class EmbyListingsNorthAmerica : IEmbyListingProvider - { - private readonly IHttpClient _httpClient; - private readonly IJsonSerializer _jsonSerializer; - - public EmbyListingsNorthAmerica(IHttpClient httpClient, IJsonSerializer jsonSerializer) - { - _httpClient = httpClient; - _jsonSerializer = jsonSerializer; - } - - public async Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken) - { - channelNumber = NormalizeNumber(channelNumber); - - var url = "https://data.emby.media/service/listings?id=" + info.ListingsId; - - // Normalize - startDateUtc = startDateUtc.Date; - endDateUtc = startDateUtc.AddDays(7); - - url += "&start=" + startDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z"; - url += "&end=" + endDateUtc.ToString("s", CultureInfo.InvariantCulture) + "Z"; - - var response = await GetResponse<ListingInfo[]>(url).ConfigureAwait(false); - - return response.Where(i => IncludeInResults(i, channelNumber)).Select(GetProgramInfo).OrderBy(i => i.StartDate); - } - - private ProgramInfo GetProgramInfo(ListingInfo info) - { - var showType = info.showType ?? string.Empty; - - var program = new ProgramInfo - { - Id = info.listingID.ToString(CultureInfo.InvariantCulture), - Name = GetStringValue(info.showName), - HomePageUrl = GetStringValue(info.webLink), - Overview = info.description, - IsHD = info.hd, - IsLive = info.live, - IsPremiere = info.seasonPremiere || info.seriesPremiere, - IsMovie = showType.IndexOf("Movie", StringComparison.OrdinalIgnoreCase) != -1, - IsKids = showType.IndexOf("Children", StringComparison.OrdinalIgnoreCase) != -1, - IsNews = showType.IndexOf("News", StringComparison.OrdinalIgnoreCase) != -1, - IsSports = showType.IndexOf("Sports", StringComparison.OrdinalIgnoreCase) != -1 - }; - - if (!string.IsNullOrWhiteSpace(info.listDateTime)) - { - program.StartDate = DateTime.ParseExact(info.listDateTime, "yyyy'-'MM'-'dd' 'HH':'mm':'ss", CultureInfo.InvariantCulture); - program.StartDate = DateTime.SpecifyKind(program.StartDate, DateTimeKind.Utc); - program.EndDate = program.StartDate.AddMinutes(info.duration); - } - - if (info.starRating > 0) - { - program.CommunityRating = info.starRating*2; - } - - if (!string.IsNullOrWhiteSpace(info.rating)) - { - // They don't have dashes so try to normalize - program.OfficialRating = info.rating.Replace("TV", "TV-").Replace("--", "-"); - - var invalid = new[] { "N/A", "Approved", "Not Rated" }; - if (invalid.Contains(program.OfficialRating, StringComparer.OrdinalIgnoreCase)) - { - program.OfficialRating = null; - } - } - - if (!string.IsNullOrWhiteSpace(info.year)) - { - program.ProductionYear = int.Parse(info.year, CultureInfo.InvariantCulture); - } - - if (info.showID > 0) - { - program.ShowId = info.showID.ToString(CultureInfo.InvariantCulture); - } - - if (info.seriesID > 0) - { - program.SeriesId = info.seriesID.ToString(CultureInfo.InvariantCulture); - program.IsSeries = true; - program.IsRepeat = info.repeat; - - program.EpisodeTitle = GetStringValue(info.episodeTitle); - - if (string.Equals(program.Name, program.EpisodeTitle, StringComparison.OrdinalIgnoreCase)) - { - program.EpisodeTitle = null; - } - } - - if (info.starRating > 0) - { - program.CommunityRating = info.starRating * 2; - } - - if (string.Equals(info.showName, "Movie", StringComparison.OrdinalIgnoreCase)) - { - // Sometimes the movie title will be in here - if (!string.IsNullOrWhiteSpace(info.episodeTitle)) - { - program.Name = info.episodeTitle; - } - } - - return program; - } - - private string GetStringValue(string s) - { - return string.IsNullOrWhiteSpace(s) ? null : s; - } - - private bool IncludeInResults(ListingInfo info, string itemNumber) - { - if (string.Equals(itemNumber, NormalizeNumber(info.number), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - var channelNumber = info.channelNumber.ToString(CultureInfo.InvariantCulture); - if (info.subChannelNumber > 0) - { - channelNumber += "." + info.subChannelNumber.ToString(CultureInfo.InvariantCulture); - } - - return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase); - } - - public async Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken) - { - var response = await GetResponse<LineupDetailResponse>("https://data.emby.media/service/lineups?id=" + info.ListingsId).ConfigureAwait(false); - - foreach (var channel in channels) - { - var station = response.stations.FirstOrDefault(i => - { - var itemNumber = NormalizeNumber(channel.Number); - - if (string.Equals(itemNumber, NormalizeNumber(i.number), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - var channelNumber = i.channelNumber.ToString(CultureInfo.InvariantCulture); - if (i.subChannelNumber > 0) - { - channelNumber += "." + i.subChannelNumber.ToString(CultureInfo.InvariantCulture); - } - - return string.Equals(channelNumber, itemNumber, StringComparison.OrdinalIgnoreCase); - }); - - if (station != null) - { - //channel.Name = station.name; - - if (!string.IsNullOrWhiteSpace(station.logoFilename)) - { - channel.HasImage = true; - channel.ImageUrl = "http://cdn.tvpassport.com/image/station/100x100/" + station.logoFilename; - } - } - } - } - - private string NormalizeNumber(string number) - { - return number.Replace('-', '.'); - } - - public Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings) - { - return Task.FromResult(true); - } - - public async Task<List<NameIdPair>> GetLineups(string country, string location) - { - // location = postal code - var response = await GetResponse<LineupInfo[]>("https://data.emby.media/service/lineups?postalCode=" + location).ConfigureAwait(false); - - return response.Select(i => new NameIdPair - { - Name = GetName(i), - Id = i.lineupID - - }).OrderBy(i => i.Name).ToList(); - } - - private string GetName(LineupInfo info) - { - var name = info.lineupName; - - if (string.Equals(info.lineupType, "cab", StringComparison.OrdinalIgnoreCase)) - { - name += " - Cable"; - } - else if (string.Equals(info.lineupType, "sat", StringComparison.OrdinalIgnoreCase)) - { - name += " - SAT"; - } - else if (string.Equals(info.lineupType, "ota", StringComparison.OrdinalIgnoreCase)) - { - name += " - OTA"; - } - - return name; - } - - private async Task<T> GetResponse<T>(string url, Func<string, string> filter = null) - where T : class - { - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = url, - CacheLength = TimeSpan.FromDays(1), - CacheMode = CacheMode.Unconditional - - }).ConfigureAwait(false)) - { - using (var reader = new StreamReader(stream)) - { - var path = await reader.ReadToEndAsync().ConfigureAwait(false); - - using (var secondStream = await _httpClient.Get(new HttpRequestOptions - { - Url = "https://www.mb3admin.com" + path, - CacheLength = TimeSpan.FromDays(1), - CacheMode = CacheMode.Unconditional - - }).ConfigureAwait(false)) - { - return ParseResponse<T>(secondStream, filter); - } - } - } - } - - private T ParseResponse<T>(Stream response, Func<string,string> filter) - { - using (var reader = new StreamReader(response)) - { - var json = reader.ReadToEnd(); - - if (filter != null) - { - json = filter(json); - } - - return _jsonSerializer.DeserializeFromString<T>(json); - } - } - - private class LineupInfo - { - public string lineupID { get; set; } - public string lineupName { get; set; } - public string lineupType { get; set; } - public string providerID { get; set; } - public string providerName { get; set; } - public string serviceArea { get; set; } - public string country { get; set; } - } - - private class Station - { - public string number { get; set; } - public int channelNumber { get; set; } - public int subChannelNumber { get; set; } - public int stationID { get; set; } - public string name { get; set; } - public string callsign { get; set; } - public string network { get; set; } - public string stationType { get; set; } - public int NTSC_TSID { get; set; } - public int DTV_TSID { get; set; } - public string webLink { get; set; } - public string logoFilename { get; set; } - } - - private class LineupDetailResponse - { - public string lineupID { get; set; } - public string lineupName { get; set; } - public string lineupType { get; set; } - public string providerID { get; set; } - public string providerName { get; set; } - public string serviceArea { get; set; } - public string country { get; set; } - public List<Station> stations { get; set; } - } - - private class ListingInfo - { - public string number { get; set; } - public int channelNumber { get; set; } - public int subChannelNumber { get; set; } - public int stationID { get; set; } - public string name { get; set; } - public string callsign { get; set; } - public string network { get; set; } - public string stationType { get; set; } - public string webLink { get; set; } - public string logoFilename { get; set; } - public int listingID { get; set; } - public string listDateTime { get; set; } - public int duration { get; set; } - public int showID { get; set; } - public int seriesID { get; set; } - public string showName { get; set; } - public string episodeTitle { get; set; } - public string episodeNumber { get; set; } - public int parts { get; set; } - public int partNum { get; set; } - public bool seriesPremiere { get; set; } - public bool seasonPremiere { get; set; } - public bool seriesFinale { get; set; } - public bool seasonFinale { get; set; } - public bool repeat { get; set; } - public bool @new { get; set; } - public string rating { get; set; } - public bool captioned { get; set; } - public bool educational { get; set; } - public bool blackWhite { get; set; } - public bool subtitled { get; set; } - public bool live { get; set; } - public bool hd { get; set; } - public bool descriptiveVideo { get; set; } - public bool inProgress { get; set; } - public string showTypeID { get; set; } - public int breakoutLevel { get; set; } - public string showType { get; set; } - public string year { get; set; } - public string guest { get; set; } - public string cast { get; set; } - public string director { get; set; } - public int starRating { get; set; } - public string description { get; set; } - public string league { get; set; } - public string team1 { get; set; } - public string team2 { get; set; } - public string @event { get; set; } - public string location { get; set; } - } - } -} diff --git a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/IEmbyListingProvider.cs b/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/IEmbyListingProvider.cs deleted file mode 100644 index 95c22b986..000000000 --- a/MediaBrowser.Server.Implementations/LiveTv/Listings/Emby/IEmbyListingProvider.cs +++ /dev/null @@ -1,18 +0,0 @@ -using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.LiveTv; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Server.Implementations.LiveTv.Listings.Emby -{ - public interface IEmbyListingProvider - { - Task<IEnumerable<ProgramInfo>> GetProgramsAsync(ListingsProviderInfo info, string channelNumber, DateTime startDateUtc, DateTime endDateUtc, CancellationToken cancellationToken); - Task AddMetadata(ListingsProviderInfo info, List<ChannelInfo> channels, CancellationToken cancellationToken); - Task Validate(ListingsProviderInfo info, bool validateLogin, bool validateListings); - Task<List<NameIdPair>> GetLineups(string country, string location); - } -} diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index e09e06bd4..301fbe360 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -560,6 +560,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv } item.ExternalId = channelInfo.Id; + if (!item.ParentId.Equals(parentFolderId)) + { + isNew = true; + } + item.ParentId = parentFolderId; + item.ChannelType = channelInfo.ChannelType; item.ServiceName = serviceName; item.Number = channelInfo.Number; @@ -622,6 +628,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv }; } + if (!item.ParentId.Equals(channel.Id)) + { + forceUpdate = true; + } + item.ParentId = channel.Id; + //item.ChannelType = channelType; if (!string.Equals(item.ServiceName, serviceName, StringComparison.Ordinal)) { @@ -774,6 +786,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv } recording.IsSeries = info.IsSeries; + if (!item.ParentId.Equals(parentFolderId)) + { + dataChanged = true; + } + item.ParentId = parentFolderId; + if (!item.HasImage(ImageType.Primary)) { if (!string.IsNullOrWhiteSpace(info.ImagePath)) @@ -851,7 +869,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId); - var internalQuery = new InternalItemsQuery + var internalQuery = new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, MinEndDate = query.MinEndDate, @@ -869,16 +887,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv SortOrder = query.SortOrder ?? SortOrder.Ascending }; - if (user != null) - { - internalQuery.MaxParentalRating = user.Policy.MaxParentalRating; - - if (user.Policy.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram)) - { - internalQuery.HasParentalRating = true; - } - } - if (query.HasAired.HasValue) { if (query.HasAired.Value) @@ -913,7 +921,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv { var user = _userManager.GetUserById(query.UserId); - var internalQuery = new InternalItemsQuery + var internalQuery = new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, IsAiring = query.IsAiring, @@ -922,16 +930,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv IsKids = query.IsKids }; - if (user != null) - { - internalQuery.MaxParentalRating = user.Policy.MaxParentalRating; - - if (user.Policy.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram)) - { - internalQuery.HasParentalRating = true; - } - } - if (query.HasAired.HasValue) { if (query.HasAired.Value) @@ -1399,7 +1397,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv await RefreshRecordings(cancellationToken).ConfigureAwait(false); - var internalQuery = new InternalItemsQuery + var internalQuery = new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name } }; @@ -1409,8 +1407,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv internalQuery.ChannelIds = new[] { query.ChannelId }; } - var queryResult = _libraryManager.GetItems(internalQuery); - IEnumerable<ILiveTvRecording> recordings = queryResult.Items.Cast<ILiveTvRecording>(); + var queryResult = _libraryManager.GetItems(internalQuery, new string[] { }); + IEnumerable<ILiveTvRecording> recordings = queryResult.Cast<ILiveTvRecording>(); if (!string.IsNullOrEmpty(query.Id)) { @@ -1812,7 +1810,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var now = DateTime.UtcNow; - var programs = _libraryManager.GetItems(new InternalItemsQuery + var programs = _libraryManager.GetItems(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, ChannelIds = new[] { id }, @@ -1821,7 +1819,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv Limit = 1, SortBy = new[] { "StartDate" } - }).Items.Cast<LiveTvProgram>(); + }, new string[] { }).Cast<LiveTvProgram>(); var currentProgram = programs.FirstOrDefault(); @@ -1836,7 +1834,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv var now = DateTime.UtcNow; - var programs = _libraryManager.GetItems(new InternalItemsQuery + var programs = _libraryManager.GetItems(new InternalItemsQuery(user) { IncludeItemTypes = new[] { typeof(LiveTvProgram).Name }, ChannelIds = new[] { channel.Id.ToString("N") }, @@ -1845,7 +1843,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv Limit = 1, SortBy = new[] { "StartDate" } - }).Items.Cast<LiveTvProgram>(); + }, new string[] { }).Cast<LiveTvProgram>(); var currentProgram = programs.FirstOrDefault(); diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index b264233eb..5afa4e1ec 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -52,15 +52,19 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\MediaBrowser.Naming.1.0.0.41\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath> </Reference> + <Reference Include="MoreLinq, Version=1.4.18916.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> + </Reference> <Reference Include="Patterns.Logging"> <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> </Reference> <Reference Include="ServiceStack.Api.Swagger"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Api.Swagger.dll</HintPath> </Reference> - <Reference Include="SocketHttpListener, Version=1.0.5754.42244, Culture=neutral, processorArchitecture=MSIL"> + <Reference Include="SocketHttpListener, Version=1.0.5798.24687, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SocketHttpListener.1.0.0.10\lib\net45\SocketHttpListener.dll</HintPath> + <HintPath>..\packages\SocketHttpListener.1.0.0.16\lib\net45\SocketHttpListener.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> @@ -70,6 +74,7 @@ <Reference Include="Microsoft.CSharp" /> <Reference Include="System.Data" /> <Reference Include="System.Net" /> + <Reference Include="System.Runtime.Serialization" /> <Reference Include="System.Security" /> <Reference Include="System.Web" /> <Reference Include="System.Xml" /> @@ -94,9 +99,6 @@ <Reference Include="Mono.Nat"> <HintPath>..\packages\Mono.Nat.1.2.24.0\lib\net40\Mono.Nat.dll</HintPath> </Reference> - <Reference Include="MoreLinq"> - <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> @@ -120,9 +122,9 @@ <Compile Include="Connect\ConnectManager.cs" /> <Compile Include="Connect\Responses.cs" /> <Compile Include="Connect\Validator.cs" /> + <Compile Include="Devices\CameraUploadsFolder.cs" /> <Compile Include="Devices\DeviceManager.cs" /> <Compile Include="Devices\DeviceRepository.cs" /> - <Compile Include="Devices\CameraUploadsFolder.cs" /> <Compile Include="Dto\DtoService.cs" /> <Compile Include="EntryPoints\ActivityLogEntryPoint.cs" /> <Compile Include="EntryPoints\AutomaticRestartEntryPoint.cs" /> @@ -217,9 +219,6 @@ <Compile Include="LiveTv\EmbyTV\RecordingHelper.cs" /> <Compile Include="LiveTv\EmbyTV\SeriesTimerManager.cs" /> <Compile Include="LiveTv\EmbyTV\TimerManager.cs" /> - <Compile Include="LiveTv\Listings\Emby\EmbyListings.cs" /> - <Compile Include="LiveTv\Listings\Emby\EmbyListingsNorthAmerica.cs" /> - <Compile Include="LiveTv\Listings\Emby\IEmbyListingProvider.cs" /> <Compile Include="LiveTv\Listings\SchedulesDirect.cs" /> <Compile Include="LiveTv\Listings\XmlTv.cs" /> <Compile Include="LiveTv\LiveTvConfigurationFactory.cs" /> diff --git a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs index 070c5239e..69ddb4d13 100644 --- a/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities.Audio; namespace MediaBrowser.Server.Implementations.Persistence @@ -24,6 +25,8 @@ namespace MediaBrowser.Server.Implementations.Persistence private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; + public const int MigrationVersion = 7; + public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem) { _libraryManager = libraryManager; @@ -64,16 +67,15 @@ namespace MediaBrowser.Server.Implementations.Persistence innerProgress.RegisterAction(p => progress.Report(45 + (.55 * p))); await CleanDeletedItems(cancellationToken, innerProgress).ConfigureAwait(false); progress.Report(100); + + await _itemRepo.UpdateInheritedValues(cancellationToken).ConfigureAwait(false); } private async Task UpdateToLatestSchema(CancellationToken cancellationToken, IProgress<double> progress) { var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery { - IsCurrentSchema = false, - - // These are constantly getting regenerated so don't bother with them here - ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name } + IsCurrentSchema = false }); var numComplete = 0; @@ -115,9 +117,9 @@ namespace MediaBrowser.Server.Implementations.Persistence progress.Report(percent * 100); } - if (!_config.Configuration.DisableStartupScan) + if (_config.Configuration.MigrationVersion < MigrationVersion) { - _config.Configuration.DisableStartupScan = true; + _config.Configuration.MigrationVersion = MigrationVersion; _config.SaveConfiguration(); } @@ -165,12 +167,22 @@ namespace MediaBrowser.Server.Implementations.Persistence { var result = _itemRepo.GetItemIdsWithPath(new InternalItemsQuery { - IsOffline = false, LocationType = LocationType.FileSystem, //Limit = limit, // These have their own cleanup routines - ExcludeItemTypes = new[] { typeof(Person).Name, typeof(Genre).Name, typeof(MusicGenre).Name, typeof(GameGenre).Name, typeof(Studio).Name, typeof(Year).Name } + ExcludeItemTypes = new[] + { + typeof(Person).Name, + typeof(Genre).Name, + typeof(MusicGenre).Name, + typeof(GameGenre).Name, + typeof(Studio).Name, + typeof(Year).Name, + typeof(Channel).Name, + typeof(AggregateFolder).Name, + typeof(CollectionFolder).Name + } }); var numComplete = 0; @@ -191,6 +203,11 @@ namespace MediaBrowser.Server.Implementations.Persistence var libraryItem = _libraryManager.GetItemById(item.Item1); + if (libraryItem.IsTopParent) + { + continue; + } + if (Folder.IsPathOffline(path)) { libraryItem.IsOffline = true; @@ -229,4 +246,4 @@ namespace MediaBrowser.Server.Implementations.Persistence }; } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs index f54e702d6..c983dd547 100644 --- a/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs +++ b/MediaBrowser.Server.Implementations/Persistence/MediaStreamColumns.cs @@ -27,6 +27,38 @@ namespace MediaBrowser.Server.Implementations.Persistence AddIsCabacColumn(); AddKeyFramesColumn(); AddRefFramesCommand(); + AddCodecTagColumn(); + } + + private void AddCodecTagColumn() + { + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "PRAGMA table_info(mediastreams)"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + if (!reader.IsDBNull(1)) + { + var name = reader.GetString(1); + + if (string.Equals(name, "CodecTag", StringComparison.OrdinalIgnoreCase)) + { + return; + } + } + } + } + } + + var builder = new StringBuilder(); + + builder.AppendLine("alter table mediastreams"); + builder.AppendLine("add column CodecTag TEXT"); + + _connection.RunQueries(new[] { builder.ToString() }, _logger); } private void AddPixelFormatColumnCommand() diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs index adaab5b8a..6b834bbf2 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs @@ -19,6 +19,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Channels; +using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Server.Implementations.Persistence { @@ -76,7 +77,12 @@ namespace MediaBrowser.Server.Implementations.Persistence private IDbCommand _deleteStreamsCommand; private IDbCommand _saveStreamCommand; - private const int LatestSchemaVersion = 13; + private IDbCommand _deleteAncestorsCommand; + private IDbCommand _saveAncestorCommand; + + private IDbCommand _updateInheritedRatingCommand; + + private const int LatestSchemaVersion = 40; /// <summary> /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class. @@ -121,17 +127,24 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection = await SqliteExtensions.ConnectToDb(dbFile, _logger).ConfigureAwait(false); var createMediaStreamsTableCommand - = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; + = "create table if not exists mediastreams (ItemId GUID, StreamIndex INT, StreamType TEXT, Codec TEXT, Language TEXT, ChannelLayout TEXT, Profile TEXT, AspectRatio TEXT, Path TEXT, IsInterlaced BIT, BitRate INT NULL, Channels INT NULL, SampleRate INT NULL, IsDefault BIT, IsForced BIT, IsExternal BIT, Height INT NULL, Width INT NULL, AverageFrameRate FLOAT NULL, RealFrameRate FLOAT NULL, Level FLOAT NULL, PixelFormat TEXT, BitDepth INT NULL, IsAnamorphic BIT NULL, RefFrames INT NULL, IsCabac BIT NULL, KeyFrames TEXT NULL, CodecTag TEXT NULL, PRIMARY KEY (ItemId, StreamIndex))"; string[] queries = { - "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB)", + "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID)", "create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)", + "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)", + "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, AncestorIdText TEXT, PRIMARY KEY (ItemId, AncestorId))", + "create index if not exists idx_AncestorIds1 on AncestorIds(AncestorId)", + "create index if not exists idx_AncestorIds2 on AncestorIds(AncestorIdText)", + "create table if not exists ChildrenIds (ParentId GUID, ItemId GUID, PRIMARY KEY (ParentId, ItemId))", "create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)", "create table if not exists People (ItemId GUID, Name TEXT NOT NULL, Role TEXT, PersonType TEXT, SortOrder int, ListOrder int)", + "create index if not exists idxPeopleItemId on People(ItemId)", + "create index if not exists idxPeopleName on People(Name)", "create table if not exists "+ChaptersTableName+" (ItemId GUID, ChapterIndex INT, StartPositionTicks BIGINT, Name TEXT, ImagePath TEXT, PRIMARY KEY (ItemId, ChapterIndex))", "create index if not exists idx_"+ChaptersTableName+" on "+ChaptersTableName+"(ItemId, ChapterIndex)", @@ -147,6 +160,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.RunQueries(queries, _logger); + _connection.AddColumn(_logger, "AncestorIds", "AncestorIdText", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "Path", "Text"); _connection.AddColumn(_logger, "TypedBaseItems", "StartDate", "DATETIME"); _connection.AddColumn(_logger, "TypedBaseItems", "EndDate", "DATETIME"); @@ -198,6 +213,18 @@ namespace MediaBrowser.Server.Implementations.Persistence _connection.AddColumn(_logger, "TypedBaseItems", "ExternalEtag", "Text"); _connection.AddColumn(_logger, "TypedBaseItems", "DateLastRefreshed", "DATETIME"); + _connection.AddColumn(_logger, "TypedBaseItems", "DateLastSaved", "DATETIME"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsInMixedFolder", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "LockedFields", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "Studios", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "Audio", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "ExternalServiceId", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "Tags", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "IsFolder", "BIT"); + _connection.AddColumn(_logger, "TypedBaseItems", "InheritedParentalRatingValue", "INT"); + _connection.AddColumn(_logger, "TypedBaseItems", "UnratedType", "Text"); + _connection.AddColumn(_logger, "TypedBaseItems", "TopParentId", "Text"); + PrepareStatements(); new MediaStreamColumns(_connection, _logger).AddColumns(); @@ -307,7 +334,27 @@ namespace MediaBrowser.Server.Implementations.Persistence "PreferredMetadataCountryCode", "IsHD", "ExternalEtag", - "DateLastRefreshed" + "DateLastRefreshed", + "Name", + "Path", + "PremiereDate", + "Overview", + "ParentIndexNumber", + "ProductionYear", + "OfficialRating", + "OfficialRatingDescription", + "HomePageUrl", + "DisplayMediaType", + "ForcedSortName", + "RunTimeTicks", + "VoteCount", + "DateCreated", + "DateModified", + "guid", + "Genres", + "ParentId", + "Audio", + "ExternalServiceId" }; private readonly string[] _mediaStreamSaveColumns = @@ -338,7 +385,8 @@ namespace MediaBrowser.Server.Implementations.Persistence "IsAnamorphic", "RefFrames", "IsCabac", - "KeyFrames" + "KeyFrames", + "CodecTag" }; /// <summary> @@ -378,6 +426,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "ParentId", "Genres", "ParentalRatingValue", + "InheritedParentalRatingValue", "SchemaVersion", "SortName", "RunTimeTicks", @@ -394,7 +443,17 @@ namespace MediaBrowser.Server.Implementations.Persistence "PreferredMetadataCountryCode", "IsHD", "ExternalEtag", - "DateLastRefreshed" + "DateLastRefreshed", + "DateLastSaved", + "IsInMixedFolder", + "LockedFields", + "Studios", + "Audio", + "ExternalServiceId", + "Tags", + "IsFolder", + "UnratedType", + "TopParentId" }; _saveItemCommand = _connection.CreateCommand(); _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values ("; @@ -438,6 +497,17 @@ namespace MediaBrowser.Server.Implementations.Persistence _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder"); _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder"); + // Ancestors + _deleteAncestorsCommand = _connection.CreateCommand(); + _deleteAncestorsCommand.CommandText = "delete from AncestorIds where ItemId=@Id"; + _deleteAncestorsCommand.Parameters.Add(_deleteAncestorsCommand, "@Id"); + + _saveAncestorCommand = _connection.CreateCommand(); + _saveAncestorCommand.CommandText = "insert into AncestorIds (ItemId, AncestorId, AncestorIdText) values (@ItemId, @AncestorId, @AncestorIdText)"; + _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@ItemId"); + _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@AncestorId"); + _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@AncestorIdText"); + // Chapters _deleteChaptersCommand = _connection.CreateCommand(); _deleteChaptersCommand.CommandText = "delete from " + ChaptersTableName + " where ItemId=@ItemId"; @@ -467,6 +537,11 @@ namespace MediaBrowser.Server.Implementations.Persistence { _saveStreamCommand.Parameters.Add(_saveStreamCommand, "@" + col); } + + _updateInheritedRatingCommand = _connection.CreateCommand(); + _updateInheritedRatingCommand.CommandText = "Update TypedBaseItems set InheritedParentalRatingValue=@InheritedParentalRatingValue where Guid=@Guid"; + _updateInheritedRatingCommand.Parameters.Add(_updateInheritedRatingCommand, "@InheritedParentalRatingValue"); + _updateInheritedRatingCommand.Parameters.Add(_updateInheritedRatingCommand, "@Guid"); } /// <summary> @@ -592,7 +667,8 @@ namespace MediaBrowser.Server.Implementations.Persistence } _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Genres.ToArray()); - _saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue(); + _saveItemCommand.GetParameter(index++).Value = item.GetParentalRatingValue() ?? 0; + _saveItemCommand.GetParameter(index++).Value = item.GetInheritedParentalRatingValue() ?? 0; _saveItemCommand.GetParameter(index++).Value = LatestSchemaVersion; _saveItemCommand.GetParameter(index++).Value = item.SortName; @@ -623,9 +699,53 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveItemCommand.GetParameter(index++).Value = item.DateLastRefreshed; } + _saveItemCommand.GetParameter(index++).Value = item.DateLastSaved; + _saveItemCommand.GetParameter(index++).Value = item.IsInMixedFolder; + _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.LockedFields.Select(i => i.ToString()).ToArray()); + _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Studios.ToArray()); + + if (item.Audio.HasValue) + { + _saveItemCommand.GetParameter(index++).Value = item.Audio.Value.ToString(); + } + else + { + _saveItemCommand.GetParameter(index++).Value = null; + } + + var tvItem = item as ILiveTvItem; + if (tvItem != null) + { + _saveItemCommand.GetParameter(index++).Value = tvItem.ServiceName; + } + else + { + _saveItemCommand.GetParameter(index++).Value = null; + } + + _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Tags.ToArray()); + _saveItemCommand.GetParameter(index++).Value = item.IsFolder; + + _saveItemCommand.GetParameter(index++).Value = item.GetBlockUnratedType().ToString(); + + var topParent = item.GetTopParent(); + if (topParent != null) + { + _saveItemCommand.GetParameter(index++).Value = topParent.Id.ToString("N"); + } + else + { + _saveItemCommand.GetParameter(index++).Value = null; + } + _saveItemCommand.Transaction = transaction; _saveItemCommand.ExecuteNonQuery(); + + if (item.SupportsAncestors) + { + UpdateAncestors(item.Id, item.GetAncestorIds().Distinct().ToList(), transaction); + } } transaction.Commit(); @@ -706,22 +826,32 @@ namespace MediaBrowser.Server.Implementations.Persistence return null; } - BaseItem item; + BaseItem item = null; using (var stream = reader.GetMemoryStream(1)) { try { item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem; + } + catch (SerializationException ex) + { + _logger.ErrorException("Error deserializing item", ex); + } - if (item == null) + if (item == null) + { + try + { + item = Activator.CreateInstance(type) as BaseItem; + } + catch { - return null; } } - catch (SerializationException ex) + + if (item == null) { - _logger.ErrorException("Error deserializing item", ex); return null; } } @@ -844,6 +974,107 @@ namespace MediaBrowser.Server.Implementations.Persistence item.DateLastRefreshed = reader.GetDateTime(23).ToUniversalTime(); } + if (!reader.IsDBNull(24)) + { + item.Name = reader.GetString(24); + } + + if (!reader.IsDBNull(25)) + { + item.Path = reader.GetString(25); + } + + if (!reader.IsDBNull(26)) + { + item.PremiereDate = reader.GetDateTime(26).ToUniversalTime(); + } + + if (!reader.IsDBNull(27)) + { + item.Overview = reader.GetString(27); + } + + if (!reader.IsDBNull(28)) + { + item.ParentIndexNumber = reader.GetInt32(28); + } + + if (!reader.IsDBNull(29)) + { + item.ProductionYear = reader.GetInt32(29); + } + + if (!reader.IsDBNull(30)) + { + item.OfficialRating = reader.GetString(30); + } + + if (!reader.IsDBNull(31)) + { + item.OfficialRating = reader.GetString(31); + } + + if (!reader.IsDBNull(32)) + { + item.HomePageUrl = reader.GetString(32); + } + + if (!reader.IsDBNull(33)) + { + item.DisplayMediaType = reader.GetString(33); + } + + if (!reader.IsDBNull(34)) + { + item.ForcedSortName = reader.GetString(34); + } + + if (!reader.IsDBNull(35)) + { + item.RunTimeTicks = reader.GetInt64(35); + } + + if (!reader.IsDBNull(36)) + { + item.VoteCount = reader.GetInt32(36); + } + + if (!reader.IsDBNull(37)) + { + item.DateCreated = reader.GetDateTime(37).ToUniversalTime(); + } + + if (!reader.IsDBNull(38)) + { + item.DateModified = reader.GetDateTime(38).ToUniversalTime(); + } + + item.Id = reader.GetGuid(39); + + if (!reader.IsDBNull(40)) + { + item.Genres = reader.GetString(40).Split('|').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); + } + + if (!reader.IsDBNull(41)) + { + item.ParentId = reader.GetGuid(41); + } + + if (!reader.IsDBNull(42)) + { + item.Audio = (ProgramAudio)Enum.Parse(typeof(ProgramAudio), reader.GetString(42), true); + } + + if (!reader.IsDBNull(43)) + { + var tvItem = item as ILiveTvItem; + if (tvItem != null) + { + tvItem.ServiceName = reader.GetString(43); + } + } + return item; } @@ -1168,6 +1399,8 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId; + //_logger.Debug(cmd.CommandText); + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) { while (reader.Read()) @@ -1213,6 +1446,50 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query) + { + if (query == null) + { + throw new ArgumentNullException("query"); + } + + CheckDisposed(); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems"; + + var whereClauses = GetWhereClauses(query, cmd, true); + + var whereText = whereClauses.Count == 0 ? + string.Empty : + " where " + string.Join(" AND ", whereClauses.ToArray()); + + cmd.CommandText += whereText; + + cmd.CommandText += GetOrderByText(query); + + if (query.Limit.HasValue) + { + cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture); + } + + //_logger.Debug(cmd.CommandText); + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + var item = GetItem(reader); + if (item != null) + { + yield return item; + } + } + } + } + } + public QueryResult<BaseItem> GetItems(InternalItemsQuery query) { if (query == null) @@ -1293,6 +1570,12 @@ namespace MediaBrowser.Server.Implementations.Persistence private string MapOrderByField(string name) { + if (string.Equals(name, "airtime", StringComparison.OrdinalIgnoreCase)) + { + // TODO + return "SortName"; + } + return name; } @@ -1328,7 +1611,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _logger.Debug(cmd.CommandText); - using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess)) + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) { while (reader.Read()) { @@ -1512,6 +1795,11 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("IsSports=@IsSports"); cmd.Parameters.Add(cmd, "@IsSports", DbType.Boolean).Value = query.IsSports; } + if (query.IsFolder.HasValue) + { + whereClauses.Add("IsFolder=@IsFolder"); + cmd.Parameters.Add(cmd, "@IsFolder", DbType.Boolean).Value = query.IsFolder; + } var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray(); if (includeTypes.Length == 1) @@ -1572,6 +1860,12 @@ namespace MediaBrowser.Server.Implementations.Persistence cmd.Parameters.Add(cmd, "@MinStartDate", DbType.Date).Value = query.MinStartDate.Value; } + if (query.MinPremiereDate.HasValue) + { + whereClauses.Add("PremiereDate>=@MinPremiereDate"); + cmd.Parameters.Add(cmd, "@MinPremiereDate", DbType.Date).Value = query.MinPremiereDate.Value; + } + if (query.MaxStartDate.HasValue) { whereClauses.Add("StartDate<=@MaxStartDate"); @@ -1623,7 +1917,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (query.MaxParentalRating.HasValue) { - whereClauses.Add("(ParentalRatingValue is NULL OR ParentalRatingValue<=@MaxParentalRating)"); + whereClauses.Add("InheritedParentalRatingValue<=@MaxParentalRating"); cmd.Parameters.Add(cmd, "@MaxParentalRating", DbType.Int32).Value = query.MaxParentalRating.Value; } @@ -1631,11 +1925,11 @@ namespace MediaBrowser.Server.Implementations.Persistence { if (query.HasParentalRating.Value) { - whereClauses.Add("ParentalRatingValue NOT NULL"); + whereClauses.Add("InheritedParentalRatingValue > 0"); } else { - whereClauses.Add("ParentalRatingValue IS NULL"); + whereClauses.Add("InheritedParentalRatingValue = 0"); } } @@ -1646,7 +1940,60 @@ namespace MediaBrowser.Server.Implementations.Persistence whereClauses.Add("ParentId NOT NULL AND ParentId NOT IN (select guid from TypedBaseItems)"); } } + if (query.ExcludeLocationTypes.Length == 1) + { + whereClauses.Add("LocationType<>@LocationType"); + cmd.Parameters.Add(cmd, "@LocationType", DbType.String).Value = query.ExcludeLocationTypes[0].ToString(); + } + if (query.ExcludeLocationTypes.Length > 1) + { + var val = string.Join(",", query.ExcludeLocationTypes.Select(i => "'" + i + "'").ToArray()); + + whereClauses.Add("LocationType not in (" + val + ")"); + } + if (query.TopParentIds.Length == 1) + { + whereClauses.Add("(TopParentId=@TopParentId)"); + cmd.Parameters.Add(cmd, "@TopParentId", DbType.String).Value = query.TopParentIds[0]; + } + if (query.TopParentIds.Length > 1) + { + var val = string.Join(",", query.TopParentIds.Select(i => "'" + i + "'").ToArray()); + + whereClauses.Add("(TopParentId in (" + val + "))"); + } + + if (query.AncestorIds.Length == 1) + { + whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)"); + cmd.Parameters.Add(cmd, "@AncestorId", DbType.Guid).Value = new Guid(query.AncestorIds[0]); + } + if (query.AncestorIds.Length > 1) + { + var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + new Guid(i).ToString("N") + "'").ToArray()); + whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorIdText in ({0}))", inClause)); + } + + if (query.BlockUnratedItems.Length == 1) + { + whereClauses.Add("(InheritedParentalRatingValue > 0 or UnratedType <> @UnratedType)"); + cmd.Parameters.Add(cmd, "@UnratedType", DbType.String).Value = query.BlockUnratedItems[0].ToString(); + } + if (query.BlockUnratedItems.Length > 1) + { + var inClause = string.Join(",", query.BlockUnratedItems.Select(i => "'" + i.ToString() + "'").ToArray()); + whereClauses.Add(string.Format("(InheritedParentalRatingValue > 0 or UnratedType not in ({0}))", inClause)); + } + + //var excludeTagIndex = 0; + //foreach (var excludeTag in query.ExcludeTags) + //{ + // whereClauses.Add("Tags not like @excludeTag" + excludeTagIndex); + // cmd.Parameters.Add(cmd, "@excludeTag" + excludeTagIndex, DbType.String).Value = "%" + excludeTag + "%"; + // excludeTagIndex++; + //} + if (addPaging) { if (query.StartIndex.HasValue && query.StartIndex.Value > 0) @@ -1703,6 +2050,83 @@ namespace MediaBrowser.Server.Implementations.Persistence typeof(AggregateFolder) }; + public async Task UpdateInheritedValues(CancellationToken cancellationToken) + { + var newValues = new List<Tuple<Guid, int>>(); + + using (var cmd = _connection.CreateCommand()) + { + cmd.CommandText = "select Guid,InheritedParentalRatingValue,(select Max(ParentalRatingValue, (select COALESCE(MAX(ParentalRatingValue),0) from TypedBaseItems where guid in (Select AncestorId from AncestorIds where ItemId=Outer.guid)))) as NewInheritedParentalRatingValue from typedbaseitems as Outer where InheritedParentalRatingValue <> NewInheritedParentalRatingValue"; + + using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)) + { + while (reader.Read()) + { + var id = reader.GetGuid(0); + var newValue = reader.GetInt32(2); + + newValues.Add(new Tuple<Guid, int>(id, newValue)); + } + } + } + + if (newValues.Count == 0) + { + return; + } + + await _writeLock.WaitAsync(cancellationToken).ConfigureAwait(false); + + IDbTransaction transaction = null; + + try + { + transaction = _connection.BeginTransaction(); + + foreach (var item in newValues) + { + _updateInheritedRatingCommand.GetParameter(0).Value = item.Item1; + _updateInheritedRatingCommand.GetParameter(1).Value = item.Item2; + + _updateInheritedRatingCommand.Transaction = transaction; + _updateInheritedRatingCommand.ExecuteNonQuery(); + + _updateInheritedRatingCommand.ExecuteNonQuery(); + } + + transaction.Commit(); + } + catch (OperationCanceledException) + { + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + catch (Exception e) + { + _logger.ErrorException("Error running query:", e); + + if (transaction != null) + { + transaction.Rollback(); + } + + throw; + } + finally + { + if (transaction != null) + { + transaction.Dispose(); + } + + _writeLock.Release(); + } + } + private static Dictionary<string, string[]> GetTypeMapDictionary() { var dict = new Dictionary<string, string[]>(); @@ -1712,6 +2136,8 @@ namespace MediaBrowser.Server.Implementations.Persistence dict[t.Name] = new[] { t.FullName }; } + dict["ChannelItem"] = new[] { typeof(ChannelVideoItem).FullName, typeof(ChannelAudioItem).FullName, typeof(ChannelFolderItem).FullName }; + dict["LiveTvItem"] = new[] { typeof(LiveTvAudioRecording).FullName, typeof(LiveTvVideoRecording).FullName, typeof(LiveTvChannel).FullName, typeof(LiveTvProgram).FullName }; dict["Recording"] = new[] { typeof(LiveTvAudioRecording).FullName, typeof(LiveTvVideoRecording).FullName }; dict["Program"] = new[] { typeof(LiveTvProgram).FullName }; dict["TvChannel"] = new[] { typeof(LiveTvChannel).FullName }; @@ -1770,11 +2196,16 @@ namespace MediaBrowser.Server.Implementations.Persistence _deleteStreamsCommand.Transaction = transaction; _deleteStreamsCommand.ExecuteNonQuery(); + // Delete ancestors + _deleteAncestorsCommand.GetParameter(0).Value = id; + _deleteAncestorsCommand.Transaction = transaction; + _deleteAncestorsCommand.ExecuteNonQuery(); + // Delete the item _deleteItemCommand.GetParameter(0).Value = id; _deleteItemCommand.Transaction = transaction; _deleteItemCommand.ExecuteNonQuery(); - + transaction.Commit(); } catch (OperationCanceledException) @@ -2003,6 +2434,38 @@ namespace MediaBrowser.Server.Implementations.Persistence return whereClauses; } + private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDbTransaction transaction) + { + if (itemId == Guid.Empty) + { + throw new ArgumentNullException("itemId"); + } + + if (ancestorIds == null) + { + throw new ArgumentNullException("ancestorIds"); + } + + CheckDisposed(); + + // First delete + _deleteAncestorsCommand.GetParameter(0).Value = itemId; + _deleteAncestorsCommand.Transaction = transaction; + + _deleteAncestorsCommand.ExecuteNonQuery(); + + foreach (var ancestorId in ancestorIds) + { + _saveAncestorCommand.GetParameter(0).Value = itemId; + _saveAncestorCommand.GetParameter(1).Value = ancestorId; + _saveAncestorCommand.GetParameter(2).Value = ancestorId.ToString("N"); + + _saveAncestorCommand.Transaction = transaction; + + _saveAncestorCommand.ExecuteNonQuery(); + } + } + public async Task UpdatePeople(Guid itemId, List<PersonInfo> people) { if (itemId == Guid.Empty) @@ -2229,6 +2692,8 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(index++).Value = string.Join(",", stream.KeyFrames.Select(i => i.ToString(CultureInfo.InvariantCulture)).ToArray()); } + _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag; + _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); } @@ -2390,6 +2855,11 @@ namespace MediaBrowser.Server.Implementations.Persistence } } + if (!reader.IsDBNull(27)) + { + item.CodecTag = reader.GetString(27); + } + return item; } diff --git a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs index 4d3e091b1..fbf514423 100644 --- a/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs +++ b/MediaBrowser.Server.Implementations/Playlists/ManualPlaylistsFolder.cs @@ -34,11 +34,6 @@ namespace MediaBrowser.Server.Implementations.Playlists } } - public override bool IsHiddenFromUser(User user) - { - return false; - } - public override string CollectionType { get { return Model.Entities.CollectionType.Playlists; } diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs index d9e9fac2a..4413a7ddf 100644 --- a/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs +++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistImageProvider.cs @@ -47,7 +47,7 @@ namespace MediaBrowser.Server.Implementations.Playlists return subItem; } - var parent = subItem.Parent; + var parent = subItem.GetParent(); if (parent != null && parent.HasImage(ImageType.Primary)) { diff --git a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs index d9b3ed755..048e2bf8d 100644 --- a/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs +++ b/MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs @@ -264,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Playlists public Folder GetPlaylistsFolder(string userId) { return _libraryManager.RootFolder.Children.OfType<PlaylistsFolder>() + .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<PlaylistsFolder>() .FirstOrDefault(); } } diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs index 2f219c299..64ae249cd 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs @@ -43,11 +43,6 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks }.ToList(); - if (!_config.Configuration.DisableStartupScan) - { - list.Add(new StartupTrigger()); - } - return list; } diff --git a/MediaBrowser.Server.Implementations/Session/SessionManager.cs b/MediaBrowser.Server.Implementations/Session/SessionManager.cs index 8d3273421..a9ce5ba54 100644 --- a/MediaBrowser.Server.Implementations/Session/SessionManager.cs +++ b/MediaBrowser.Server.Implementations/Session/SessionManager.cs @@ -1621,17 +1621,17 @@ namespace MediaBrowser.Server.Implementations.Session if (backropItem == null) { - backropItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Backdrop)); + backropItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Backdrop)); } if (thumbItem == null) { - thumbItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Thumb)); + thumbItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Thumb)); } if (logoItem == null) { - logoItem = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Logo)); + logoItem = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Logo)); } if (thumbItem != null) diff --git a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs index db6a753b7..21377da7e 100644 --- a/MediaBrowser.Server.Implementations/Sync/SyncManager.cs +++ b/MediaBrowser.Server.Implementations/Sync/SyncManager.cs @@ -341,7 +341,7 @@ namespace MediaBrowser.Server.Implementations.Sync if (primaryImage == null) { - var parentWithImage = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary)); + var parentWithImage = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary)); if (parentWithImage != null) { @@ -380,7 +380,7 @@ namespace MediaBrowser.Server.Implementations.Sync if (primaryImage == null) { - var parentWithImage = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary)); + var parentWithImage = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary)); if (parentWithImage != null) { @@ -739,10 +739,10 @@ namespace MediaBrowser.Server.Implementations.Sync var requiresSaving = false; var removeFromDevice = false; - var libraryItem = _libraryManager.GetItemById(jobItem.ItemId); - if (request.LocalItemIds.Contains(jobItem.ItemId, StringComparer.OrdinalIgnoreCase)) { + var libraryItem = _libraryManager.GetItemById(jobItem.ItemId); + var job = _repo.GetJob(jobItem.JobId); var user = _userManager.GetUserById(job.UserId); @@ -845,10 +845,10 @@ namespace MediaBrowser.Server.Implementations.Sync var requiresSaving = false; var removeFromDevice = false; - var libraryItem = _libraryManager.GetItemById(jobItem.ItemId); - if (request.SyncJobItemIds.Contains(jobItem.Id, StringComparer.OrdinalIgnoreCase)) { + var libraryItem = _libraryManager.GetItemById(jobItem.ItemId); + var job = _repo.GetJob(jobItem.JobId); var user = _userManager.GetUserById(job.UserId); diff --git a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs index f34b43e43..69d1c89fe 100644 --- a/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs +++ b/MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs @@ -36,8 +36,12 @@ namespace MediaBrowser.Server.Implementations.TV ? new string[] { } : new[] { request.ParentId }; - var items = GetAllLibraryItems(user, parentIds, i => i is Series) - .Cast<Series>(); + var items = _libraryManager.GetItems(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Series).Name }, + SortOrder = SortOrder.Ascending + + }, parentIds).Cast<Series>(); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items); @@ -54,9 +58,12 @@ namespace MediaBrowser.Server.Implementations.TV throw new ArgumentException("User not found"); } - var items = parentsFolders - .SelectMany(i => i.GetRecursiveChildren(user, s => s is Series)) - .Cast<Series>(); + var items = _libraryManager.GetItems(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Series).Name }, + SortOrder = SortOrder.Ascending + + }, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>(); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items); @@ -64,27 +71,6 @@ namespace MediaBrowser.Server.Implementations.TV return GetResult(episodes, null, request); } - private IEnumerable<BaseItem> GetAllLibraryItems(User user, string[] parentIds, Func<BaseItem,bool> filter) - { - if (parentIds.Length > 0) - { - return parentIds.SelectMany(i => - { - var folder = (Folder)_libraryManager.GetItemById(new Guid(i)); - - return folder.GetRecursiveChildren(user, filter); - - }); - } - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return user.RootFolder.GetRecursiveChildren(user, filter); - } - public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series) { // Avoid implicitly captured closure diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 7d9453822..4ea2069af 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -4,7 +4,7 @@ <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
<package id="MediaBrowser.Naming" version="1.0.0.41" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
- <package id="morelinq" version="1.1.1" targetFramework="net45" />
+ <package id="morelinq" version="1.4.0" targetFramework="net45" />
<package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
- <package id="SocketHttpListener" version="1.0.0.10" targetFramework="net45" />
+ <package id="SocketHttpListener" version="1.0.0.16" targetFramework="net45" />
</packages>
\ No newline at end of file |
