diff options
183 files changed, 4948 insertions, 3341 deletions
diff --git a/Emby.Drawing/Emby.Drawing.csproj b/Emby.Drawing/Emby.Drawing.csproj index e9911a12d..6cda8b5d0 100644 --- a/Emby.Drawing/Emby.Drawing.csproj +++ b/Emby.Drawing/Emby.Drawing.csproj @@ -37,7 +37,7 @@ </Reference> <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath> + <HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.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> diff --git a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs index 64ddf7e88..5ca674888 100644 --- a/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs +++ b/Emby.Drawing/ImageMagick/ImageMagickEncoder.cs @@ -148,7 +148,7 @@ namespace Emby.Drawing.ImageMagick DrawIndicator(originalImage, width, height, options); originalImage.CurrentImage.CompressionQuality = quality; - //originalImage.CurrentImage.StripImage(); + originalImage.CurrentImage.StripImage(); originalImage.SaveImage(outputPath); } @@ -165,7 +165,7 @@ namespace Emby.Drawing.ImageMagick DrawIndicator(wand, width, height, options); wand.CurrentImage.CompressionQuality = quality; - //wand.CurrentImage.StripImage(); + wand.CurrentImage.StripImage(); wand.SaveImage(outputPath); } @@ -176,15 +176,14 @@ namespace Emby.Drawing.ImageMagick private void ScaleImage(MagickWand wand, int width, int height) { - wand.CurrentImage.ResizeImage(width, height); - //if (_config.Configuration.EnableHighQualityImageScaling) - //{ - // wand.CurrentImage.ResizeImage(width, height); - //} - //else - //{ - // wand.CurrentImage.ScaleImage(width, height); - //} + if (_config.Configuration.EnableHighQualityImageScaling) + { + wand.CurrentImage.ResizeImage(width, height); + } + else + { + wand.CurrentImage.ScaleImage(width, height); + } } /// <summary> diff --git a/Emby.Drawing/packages.config b/Emby.Drawing/packages.config index 0fcdc278e..3b8dbcab7 100644 --- a/Emby.Drawing/packages.config +++ b/Emby.Drawing/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> - <package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" /> + <package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Api/BaseApiService.cs b/MediaBrowser.Api/BaseApiService.cs index 7a14ace77..ecab94349 100644 --- a/MediaBrowser.Api/BaseApiService.cs +++ b/MediaBrowser.Api/BaseApiService.cs @@ -198,50 +198,6 @@ namespace MediaBrowser.Api return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager)); } - protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter) - { - 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(); - } - - return folder - .GetRecursiveChildren(filter); - } - if (!string.IsNullOrWhiteSpace(userId)) - { - var user = userManager.GetUserById(userId); - - if (user == null) - { - throw new ArgumentException("User not found"); - } - - return userManager - .GetUserById(userId) - .RootFolder - .GetRecursiveChildren(user, filter) - .ToList(); - } - - return libraryManager - .RootFolder - .GetRecursiveChildren(filter); - } - /// <summary> /// Deslugs an artist name by finding the correct entry in the library /// </summary> diff --git a/MediaBrowser.Api/GamesService.cs b/MediaBrowser.Api/GamesService.cs index 93cc01079..a27c872f1 100644 --- a/MediaBrowser.Api/GamesService.cs +++ b/MediaBrowser.Api/GamesService.cs @@ -102,12 +102,16 @@ namespace MediaBrowser.Api /// <returns>System.Object.</returns> public object Get(GetGameSystemSummaries request) { - var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem) + var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(GameSystem).Name } + }; + var parentIds = new string[] { } ; + var gameSystems = _libraryManager.GetItems(query, parentIds) .Cast<GameSystem>() .ToList(); - var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); - var result = gameSystems .Select(i => GetSummary(i, user)) .ToList(); @@ -119,8 +123,15 @@ namespace MediaBrowser.Api public object Get(GetPlayerIndex request) { - var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game) - .Cast<Game>(); + var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId); + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Game).Name } + }; + var parentIds = new string[] { }; + var games = _libraryManager.GetItems(query, parentIds) + .Cast<Game>() + .ToList(); var lookup = games .ToLookup(i => i.PlayersSupported ?? -1) diff --git a/MediaBrowser.Api/Library/LibraryService.cs b/MediaBrowser.Api/Library/LibraryService.cs index 319bc13fd..80076d073 100644 --- a/MediaBrowser.Api/Library/LibraryService.cs +++ b/MediaBrowser.Api/Library/LibraryService.cs @@ -416,7 +416,7 @@ namespace MediaBrowser.Api.Library public object Get(GetMediaFolders request) { - var items = _libraryManager.GetUserRootFolder().Children.OrderBy(i => i.SortName).ToList(); + var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList(); if (request.IsHidden.HasValue) { @@ -610,7 +610,7 @@ namespace MediaBrowser.Api.Library var dtoOptions = GetDtoOptions(request); - BaseItem parent = item.Parent; + BaseItem parent = item.GetParent(); while (parent != null) { @@ -621,7 +621,7 @@ namespace MediaBrowser.Api.Library baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user)); - parent = parent.Parent; + parent = parent.GetParent(); } return baseItemDtos.ToList(); @@ -629,7 +629,7 @@ namespace MediaBrowser.Api.Library private BaseItem TranslateParentItem(BaseItem item, User user) { - if (item.Parent is AggregateFolder) + if (item.GetParent() is AggregateFolder) { return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path)); } @@ -677,6 +677,50 @@ namespace MediaBrowser.Api.Library return ToOptimizedSerializedResultUsingCache(counts); } + private IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter) + { + 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(); + } + + return folder + .GetRecursiveChildren(filter); + } + if (!string.IsNullOrWhiteSpace(userId)) + { + var user = userManager.GetUserById(userId); + + if (user == null) + { + throw new ArgumentException("User not found"); + } + + return userManager + .GetUserById(userId) + .RootFolder + .GetRecursiveChildren(user, filter) + .ToList(); + } + + return libraryManager + .RootFolder + .GetRecursiveChildren(filter); + } + private bool FilterItem(BaseItem item, GetItemCounts request, string userId) { if (!string.IsNullOrWhiteSpace(userId)) @@ -817,9 +861,9 @@ namespace MediaBrowser.Api.Library : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null) + while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null) { - item = item.Parent; + item = item.GetParent(); } var dtoOptions = GetDtoOptions(request); @@ -860,9 +904,9 @@ namespace MediaBrowser.Api.Library : (Folder)_libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null) + while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null) { - item = item.Parent; + item = item.GetParent(); } var dtoOptions = GetDtoOptions(request); diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 7be644bc8..08b99d5a4 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -49,6 +49,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath> </Reference> + <Reference Include="MoreLinq"> + <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> @@ -63,9 +66,6 @@ <Reference Include="ServiceStack.Text"> <HintPath>..\ThirdParty\ServiceStack.Text\ServiceStack.Text.dll</HintPath> </Reference> - <Reference Include="MoreLinq"> - <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs index fe8bae1a5..36cbc6ffa 100644 --- a/MediaBrowser.Api/Movies/MoviesService.cs +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -117,10 +117,7 @@ namespace MediaBrowser.Api.Movies public async Task<object> Get(GetSimilarMovies request) { var result = await GetSimilarItemsResult( - // Strip out secondary versions - request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, - - SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); return ToOptimizedSerializedResultUsingCache(result); } @@ -128,10 +125,7 @@ namespace MediaBrowser.Api.Movies public async Task<object> Get(GetSimilarTrailers request) { var result = await GetSimilarItemsResult( - // Strip out secondary versions - request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue, - - SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); + request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false); return ToOptimizedSerializedResultUsingCache(result); } @@ -140,8 +134,12 @@ namespace MediaBrowser.Api.Movies { var user = _userManager.GetUserById(request.UserId); - IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie); - + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Movie).Name } + }; + var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId }; + var movies = _libraryManager.GetItems(query, parentIds); movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies); var listEligibleForCategories = new List<BaseItem>(); @@ -184,21 +182,27 @@ namespace MediaBrowser.Api.Movies return ToOptimizedResult(result); } - private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) + private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore) { var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; var item = string.IsNullOrEmpty(request.Id) ? (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder : _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id); - - Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i); - - var inputItems = user == null - ? _libraryManager.RootFolder.GetRecursiveChildren(filter) - : user.RootFolder.GetRecursiveChildren(user, filter); - - var list = inputItems.ToList(); + + var query = new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Movie).Name } + }; + var parentIds = new string[] { }; + var list = _libraryManager.GetItems(query, parentIds) + .Where(i => + { + // Strip out secondary versions + var v = i as Video; + return v != null && !v.PrimaryVersionId.HasValue; + }) + .ToList(); if (user != null && user.Configuration.IncludeTrailersInSuggestions) { @@ -379,9 +383,10 @@ namespace MediaBrowser.Api.Movies { foreach (var name in names) { - var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery + var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery(user) { Person = name + }); var items = allMovies diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 9eb0f7d45..622478b9b 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1562,6 +1562,13 @@ namespace MediaBrowser.Api.Playback RequestedUrl = url }; + if ((Request.UserAgent ?? string.Empty).IndexOf("iphone", StringComparison.OrdinalIgnoreCase) != -1 || + (Request.UserAgent ?? string.Empty).IndexOf("ipad", StringComparison.OrdinalIgnoreCase) != -1 || + (Request.UserAgent ?? string.Empty).IndexOf("ipod", StringComparison.OrdinalIgnoreCase) != -1) + { + state.SegmentLength = 6; + } + if (!string.IsNullOrWhiteSpace(request.AudioCodec)) { state.SupportedAudioCodecs = request.AudioCodec.Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToList(); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 60412159c..766e8a003 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -111,7 +111,8 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - await WaitForMinimumSegmentCount(playlist, 3, cancellationTokenSource.Token).ConfigureAwait(false); + var waitForSegments = state.SegmentLength >= 10 ? 2 : 3; + await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); } } finally diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 302b8d834..9cb699098 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -283,7 +283,7 @@ namespace MediaBrowser.Api private T GetParentWithImage<T>(BaseItem item, ImageType type) where T : BaseItem { - return item.Parents.OfType<T>().FirstOrDefault(i => i.HasImage(type)); + return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type)); } } } diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index 399c81ae8..365a9bfa8 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -68,11 +68,7 @@ namespace MediaBrowser.Api _config.Configuration.EnableLocalizedGuids = true; _config.Configuration.MergeMetadataAndImagesByName = true; _config.Configuration.EnableStandaloneMetadata = true; - _config.Configuration.EnableLibraryMetadataSubFolder = true; _config.Configuration.EnableCustomPathSubFolders = true; - _config.Configuration.DisableXmlSavers = true; - _config.Configuration.DisableStartupScan = true; - _config.Configuration.EnableUserViews = true; _config.Configuration.EnableDateLastRefresh = true; _config.SaveConfiguration(); } diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 29a4a8bb5..2dad9533f 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -159,7 +159,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] public string StartItemId { get; set; } - + /// <summary> /// Skips over a given number of items within the results. Use for paging. /// </summary> @@ -273,29 +273,28 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode); - - var itemsList = _libraryManager - .Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending) - .Cast<Episode>() - .ToList(); - - var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList(); - var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime(); - var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList(); - previousEpisodes.AddRange(unairedEpisodes); + var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId }; - var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit); + var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) + { + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { "PremiereDate", "AirTime", "SortName" }, + SortOrder = SortOrder.Ascending, + MinPremiereDate = minPremiereDate, + StartIndex = request.StartIndex, + Limit = request.Limit + + }, parentIds); var options = GetDtoOptions(request); - var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray(); + var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray(); var result = new ItemsResult { - TotalRecordCount = itemsList.Count, + TotalRecordCount = itemsResult.TotalRecordCount, Items = returnItems }; @@ -440,7 +439,7 @@ namespace MediaBrowser.Api } episodes = season.GetEpisodes(user); - } + } else if (request.Season.HasValue) { var series = _libraryManager.GetItemById(request.Id) as Series; @@ -495,7 +494,7 @@ namespace MediaBrowser.Api .ToList(); var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit); - + var dtoOptions = GetDtoOptions(request); var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user) diff --git a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs index 4d844e6cb..aa86bfb33 100644 --- a/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs +++ b/MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs @@ -199,6 +199,8 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string Genres { get; set; } + public string GenreIds { get; set; } + [ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] public string OfficialRatings { get; set; } @@ -378,6 +380,11 @@ namespace MediaBrowser.Api.UserLibrary return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); } + public string[] GetGenreIds() + { + return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + } + public string[] GetPersonTypes() { return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); diff --git a/MediaBrowser.Api/UserLibrary/ItemsService.cs b/MediaBrowser.Api/UserLibrary/ItemsService.cs index f1168ab7f..489580555 100644 --- a/MediaBrowser.Api/UserLibrary/ItemsService.cs +++ b/MediaBrowser.Api/UserLibrary/ItemsService.cs @@ -112,6 +112,11 @@ namespace MediaBrowser.Api.UserLibrary user == null ? _libraryManager.RootFolder : user.RootFolder : parentItem; + if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase)) + { + item = user == null ? _libraryManager.RootFolder : user.RootFolder; + } + // Default list type = children if (!string.IsNullOrEmpty(request.Ids)) @@ -210,6 +215,7 @@ namespace MediaBrowser.Api.UserLibrary Tags = request.GetTags(), OfficialRatings = request.GetOfficialRatings(), Genres = request.GetGenres(), + GenreIds = request.GetGenreIds(), Studios = request.GetStudios(), StudioIds = request.GetStudioIds(), Person = request.Person, @@ -422,15 +428,6 @@ namespace MediaBrowser.Api.UserLibrary return false; } - // Min index number - if (request.MinIndexNumber.HasValue) - { - if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value)) - { - return false; - } - } - // Min official rating if (!string.IsNullOrEmpty(request.MinOfficialRating)) { diff --git a/MediaBrowser.Api/UserLibrary/UserViewsService.cs b/MediaBrowser.Api/UserLibrary/UserViewsService.cs index 9d7c38d6f..d29191db4 100644 --- a/MediaBrowser.Api/UserLibrary/UserViewsService.cs +++ b/MediaBrowser.Api/UserLibrary/UserViewsService.cs @@ -26,6 +26,8 @@ namespace MediaBrowser.Api.UserLibrary [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")] public bool? IncludeExternalContent { get; set; } + + public string PresetViews { get; set; } } [Route("/Users/{UserId}/SpecialViewOptions", "GET")] @@ -75,9 +77,24 @@ namespace MediaBrowser.Api.UserLibrary query.IncludeExternalContent = request.IncludeExternalContent.Value; } + if (!string.IsNullOrWhiteSpace(request.PresetViews)) + { + query.PresetViews = request.PresetViews.Split(','); + } + + var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty; + if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1) + { + query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; + } + //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows }; + var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false); var dtoOptions = GetDtoOptions(request); + dtoOptions.Fields = new List<ItemFields>(); + dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio); + dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId); var user = _userManager.GetUserById(request.UserId); @@ -123,7 +140,7 @@ namespace MediaBrowser.Api.UserLibrary var views = user.RootFolder .GetChildren(user, true) .OfType<Folder>() - .Where(i => !UserView.IsExcludedFromGrouping(i)) + .Where(UserView.IsEligibleForGrouping) .ToList(); var list = views @@ -141,9 +158,7 @@ namespace MediaBrowser.Api.UserLibrary private bool IsEligibleForSpecialView(ICollectionFolder view) { - var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos }; - - return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + return UserView.IsEligibleForEnhancedView(view.CollectionType); } } diff --git a/MediaBrowser.Api/packages.config b/MediaBrowser.Api/packages.config index d96012318..258a097cd 100644 --- a/MediaBrowser.Api/packages.config +++ b/MediaBrowser.Api/packages.config @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="CommonIO" version="1.0.0.5" 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" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj index d857e58b6..59e20ec6e 100644 --- a/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj +++ b/MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj @@ -51,9 +51,11 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath> </Reference> - <Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\NLog.4.1.1\lib\net45\NLog.dll</HintPath> + <Reference Include="MoreLinq"> + <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> + </Reference> + <Reference Include="NLog"> + <HintPath>..\packages\NLog.4.2.3\lib\net45\NLog.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> @@ -62,13 +64,13 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\ThirdParty\SharpCompress\SharpCompress.dll</HintPath> </Reference> - <Reference Include="SimpleInjector, Version=2.8.0.0, Culture=neutral, PublicKeyToken=984cb50dea722e99, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\SimpleInjector.3.0.5\lib\net45\SimpleInjector.dll</HintPath> + <Reference Include="SimpleInjector"> + <HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> <Reference Include="System.Net" /> <Reference Include="System.Xml" /> <Reference Include="ServiceStack.Text"> diff --git a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs index c27dfd68d..9b278b889 100644 --- a/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs +++ b/MediaBrowser.Common.Implementations/Networking/BaseNetworkManager.cs @@ -7,6 +7,7 @@ using System.Net; using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; +using MoreLinq; namespace MediaBrowser.Common.Implementations.Networking { @@ -31,14 +32,14 @@ namespace MediaBrowser.Common.Implementations.Networking } } - private volatile List<string> _localIpAddresses; + private volatile List<IPAddress> _localIpAddresses; private readonly object _localIpAddressSyncLock = new object(); /// <summary> /// Gets the machine's local ip address /// </summary> /// <returns>IPAddress.</returns> - public IEnumerable<string> GetLocalIpAddresses() + public IEnumerable<IPAddress> GetLocalIpAddresses() { if (_localIpAddresses == null) { @@ -58,25 +59,24 @@ namespace MediaBrowser.Common.Implementations.Networking return _localIpAddresses; } - private IEnumerable<string> GetLocalIpAddressesInternal() + private IEnumerable<IPAddress> GetLocalIpAddressesInternal() { var list = GetIPsDefault() - .Where(i => !IPAddress.IsLoopback(i)) - .Select(i => i.ToString()) - .Where(FilterIpAddress) .ToList(); - if (list.Count > 0) + if (list.Count == 0) { - return list; + list.AddRange(GetLocalIpAddressesFallback()); } - return GetLocalIpAddressesFallback().Where(FilterIpAddress); + return list.Where(FilterIpAddress).DistinctBy(i => i.ToString()); } - private bool FilterIpAddress(string address) + private bool FilterIpAddress(IPAddress address) { - if (address.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) + var addressString = address.ToString (); + + if (addressString.StartsWith("169.", StringComparison.OrdinalIgnoreCase)) { return false; } @@ -84,8 +84,16 @@ namespace MediaBrowser.Common.Implementations.Networking return true; } - private bool IsInPrivateAddressSpace(string endpoint) + public bool IsInPrivateAddressSpace(string endpoint) { + if (string.Equals(endpoint, "::1", StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + // Handle ipv4 mapped to ipv6 + endpoint = endpoint.Replace("::ffff:", string.Empty); + // Private address space: // http://en.wikipedia.org/wiki/Private_network @@ -96,9 +104,6 @@ namespace MediaBrowser.Common.Implementations.Networking return - // If url was requested with computer name, we may see this - endpoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 || - endpoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || endpoint.StartsWith("10.", StringComparison.OrdinalIgnoreCase) || @@ -131,26 +136,41 @@ namespace MediaBrowser.Common.Implementations.Networking throw new ArgumentNullException("endpoint"); } - if (IsInPrivateAddressSpace(endpoint)) - { - return true; - } - - const int lengthMatch = 4; - - if (endpoint.Length >= lengthMatch) + IPAddress address; + if (IPAddress.TryParse(endpoint, out address)) { - var prefix = endpoint.Substring(0, lengthMatch); + var addressString = address.ToString(); - if (GetLocalIpAddresses() - .Any(i => i.StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) + int lengthMatch = 100; + if (address.AddressFamily == AddressFamily.InterNetwork) { - return true; + lengthMatch = 4; + if (IsInPrivateAddressSpace(addressString)) + { + return true; + } + } + else if (address.AddressFamily == AddressFamily.InterNetworkV6) + { + lengthMatch = 10; + if (IsInPrivateAddressSpace(endpoint)) + { + return true; + } } - } - IPAddress address; - if (resolveHost && !IPAddress.TryParse(endpoint, out address)) + // Should be even be doing this with ipv6? + if (addressString.Length >= lengthMatch) + { + var prefix = addressString.Substring(0, lengthMatch); + + if (GetLocalIpAddresses().Any(i => i.ToString().StartsWith(prefix, StringComparison.OrdinalIgnoreCase))) + { + return true; + } + } + } + else if (resolveHost) { Uri uri; if (Uri.TryCreate(endpoint, UriKind.RelativeOrAbsolute, out uri)) @@ -188,33 +208,44 @@ namespace MediaBrowser.Common.Implementations.Networking return Dns.GetHostAddresses(hostName); } - private IEnumerable<IPAddress> GetIPsDefault() - { - foreach (var adapter in NetworkInterface.GetAllNetworkInterfaces()) - { - var props = adapter.GetIPProperties(); - var gateways = from ga in props.GatewayAddresses - where !ga.Address.Equals(IPAddress.Any) - select true; - - if (!gateways.Any()) - { - continue; - } - - foreach (var uni in props.UnicastAddresses) - { - var address = uni.Address; - if (address.AddressFamily != AddressFamily.InterNetwork) - { - continue; - } - yield return address; - } - } - } - - private IEnumerable<string> GetLocalIpAddressesFallback() + private List<IPAddress> GetIPsDefault() + { + NetworkInterface[] interfaces; + + try + { + interfaces = NetworkInterface.GetAllNetworkInterfaces(); + } + catch (Exception ex) + { + Logger.ErrorException("Error in GetAllNetworkInterfaces", ex); + return new List<IPAddress>(); + } + + return interfaces.SelectMany(network => { + + try + { + Logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus); + + var properties = network.GetIPProperties(); + + return properties.UnicastAddresses + .Select(i => i.Address) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .ToList(); + } + catch (Exception ex) + { + Logger.ErrorException("Error querying network interface", ex); + return new List<IPAddress>(); + } + + }).DistinctBy(i => i.ToString()) + .ToList(); + } + + private IEnumerable<IPAddress> GetLocalIpAddressesFallback() { var host = Dns.GetHostEntry(Dns.GetHostName()); @@ -222,7 +253,6 @@ namespace MediaBrowser.Common.Implementations.Networking // It's not fool-proof so ultimately the consumer will have to examine them and decide return host.AddressList .Where(i => i.AddressFamily == AddressFamily.InterNetwork) - .Select(i => i.ToString()) .Reverse(); } diff --git a/MediaBrowser.Common.Implementations/packages.config b/MediaBrowser.Common.Implementations/packages.config index a0711a9c7..84f663268 100644 --- a/MediaBrowser.Common.Implementations/packages.config +++ b/MediaBrowser.Common.Implementations/packages.config @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> - <package id="NLog" version="4.1.0" targetFramework="net45" /> + <package id="morelinq" version="1.4.0" targetFramework="net45" /> + <package id="NLog" version="4.2.3" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> - <package id="SimpleInjector" version="3.0.5" targetFramework="net45" /> + <package id="SimpleInjector" version="3.1.2" targetFramework="net45" /> </packages> diff --git a/MediaBrowser.Common/Net/INetworkManager.cs b/MediaBrowser.Common/Net/INetworkManager.cs index 8fc947e8f..de63ddd51 100644 --- a/MediaBrowser.Common/Net/INetworkManager.cs +++ b/MediaBrowser.Common/Net/INetworkManager.cs @@ -11,7 +11,7 @@ namespace MediaBrowser.Common.Net /// Gets the machine's local ip address /// </summary> /// <returns>IPAddress.</returns> - IEnumerable<string> GetLocalIpAddresses(); + IEnumerable<IPAddress> GetLocalIpAddresses(); /// <summary> /// Gets a random port number that is currently available @@ -26,6 +26,13 @@ namespace MediaBrowser.Common.Net string GetMacAddress(); /// <summary> + /// Determines whether [is in private address space] [the specified endpoint]. + /// </summary> + /// <param name="endpoint">The endpoint.</param> + /// <returns><c>true</c> if [is in private address space] [the specified endpoint]; otherwise, <c>false</c>.</returns> + bool IsInPrivateAddressSpace(string endpoint); + + /// <summary> /// Gets the network shares. /// </summary> /// <param name="path">The path.</param> diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 653cec901..17dcf138b 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Channels public List<ChannelMediaInfo> ChannelMediaSources { get; set; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + return UnratedItem.ChannelContent; } protected override string CreateUserDataKey() diff --git a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs index 9010470f8..f662020bb 100644 --- a/MediaBrowser.Controller/Channels/ChannelFolderItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelFolderItem.cs @@ -6,6 +6,7 @@ using System; using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Entities; namespace MediaBrowser.Controller.Channels @@ -20,6 +21,11 @@ namespace MediaBrowser.Controller.Channels return false; } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.ChannelContent; + } + [IgnoreDataMember] public override bool SupportsLocalMetadata { diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index fb545e57a..79ad4b36b 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -42,9 +42,9 @@ namespace MediaBrowser.Controller.Channels return ExternalId; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + return UnratedItem.ChannelContent; } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 43b980c20..766f1e5ed 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -27,9 +27,22 @@ namespace MediaBrowser.Controller.Entities.Audio public long? Size { get; set; } public string Container { get; set; } public int? TotalBitrate { get; set; } - public List<string> Tags { get; set; } public ExtraType? ExtraType { get; set; } + /// <summary> + /// Gets or sets the artist. + /// </summary> + /// <value>The artist.</value> + public List<string> Artists { get; set; } + + public List<string> AlbumArtists { get; set; } + + /// <summary> + /// Gets or sets the album. + /// </summary> + /// <value>The album.</value> + public string Album { get; set; } + [IgnoreDataMember] public bool IsThemeMedia { @@ -43,7 +56,6 @@ namespace MediaBrowser.Controller.Entities.Audio { Artists = new List<string>(); AlbumArtists = new List<string>(); - Tags = new List<string>(); } [IgnoreDataMember] @@ -92,14 +104,6 @@ namespace MediaBrowser.Controller.Entities.Audio locationType != LocationType.Virtual; } - /// <summary> - /// Gets or sets the artist. - /// </summary> - /// <value>The artist.</value> - public List<string> Artists { get; set; } - - public List<string> AlbumArtists { get; set; } - [IgnoreDataMember] public List<string> AllArtists { @@ -114,12 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio } } - /// <summary> - /// Gets or sets the album. - /// </summary> - /// <value>The album.</value> - public string Album { get; set; } - [IgnoreDataMember] public MusicAlbum AlbumEntity { @@ -173,9 +171,9 @@ namespace MediaBrowser.Controller.Entities.Audio return base.CreateUserDataKey(); } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Music); + return UnratedItem.Music; } public SongInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 98d1eb4ce..9a38912ea 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio { get { - return Parents.OfType<MusicArtist>().FirstOrDefault(); + return GetParents().OfType<MusicArtist>().FirstOrDefault(); } } @@ -110,13 +110,18 @@ namespace MediaBrowser.Controller.Entities.Audio return config.BlockUnratedItems.Contains(UnratedItem.Music); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + public AlbumInfo GetLookupInfo() { var id = GetItemLookupInfo<AlbumInfo>(); id.AlbumArtists = AlbumArtists; - var artist = Parents.OfType<MusicArtist>().FirstOrDefault(); + var artist = GetParents().OfType<MusicArtist>().FirstOrDefault(); if (artist != null) { diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index f6d1d32a4..02bcceada 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -138,6 +138,11 @@ namespace MediaBrowser.Controller.Entities.Audio return config.BlockUnratedItems.Contains(UnratedItem.Music); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Music; + } + public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) { var items = GetRecursiveChildren().ToList(); diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 7086ac743..379697b81 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; @@ -24,6 +23,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Model.LiveTv; namespace MediaBrowser.Controller.Entities { @@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Entities { protected BaseItem() { + Tags = new List<string>(); Genres = new List<string>(); Studios = new List<string>(); ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); @@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// The supported image extensions /// </summary> - public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" }; + public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" }; public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList(); @@ -103,7 +104,8 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the name. /// </summary> /// <value>The name.</value> - public string Name + [IgnoreDataMember] + public virtual string Name { get { @@ -122,15 +124,24 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the id. /// </summary> /// <value>The id.</value> + [IgnoreDataMember] public Guid Id { get; set; } /// <summary> /// Gets or sets a value indicating whether this instance is hd. /// </summary> /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value> + [IgnoreDataMember] public bool? IsHD { get; set; } /// <summary> + /// Gets or sets the audio. + /// </summary> + /// <value>The audio.</value> + [IgnoreDataMember] + public ProgramAudio? Audio { get; set; } + + /// <summary> /// Return the id that should be used to key display prefs for this item. /// Default is based on the type for everything except actual generic folders. /// </summary> @@ -149,6 +160,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the path. /// </summary> /// <value>The path.</value> + [IgnoreDataMember] public virtual string Path { get; set; } [IgnoreDataMember] @@ -173,7 +185,7 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Id of the program. + /// If this content came from an external service, the id of the content on that service /// </summary> [IgnoreDataMember] public string ExternalId @@ -201,11 +213,6 @@ namespace MediaBrowser.Controller.Entities } } - public virtual bool IsHiddenFromUser(User user) - { - return false; - } - [IgnoreDataMember] public virtual bool IsOwnedItem { @@ -325,12 +332,14 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the date created. /// </summary> /// <value>The date created.</value> + [IgnoreDataMember] public DateTime DateCreated { get; set; } /// <summary> /// Gets or sets the date modified. /// </summary> /// <value>The date modified.</value> + [IgnoreDataMember] public DateTime DateModified { get; set; } public DateTime DateLastSaved { get; set; } @@ -407,6 +416,7 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the name of the forced sort. /// </summary> /// <value>The name of the forced sort.</value> + [IgnoreDataMember] public string ForcedSortName { get { return _forcedSortName; } @@ -447,10 +457,7 @@ namespace MediaBrowser.Controller.Entities { var idString = Id.ToString("N"); - if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder) - { - basePath = System.IO.Path.Combine(basePath, "library"); - } + basePath = System.IO.Path.Combine(basePath, "library"); return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); } @@ -493,6 +500,7 @@ namespace MediaBrowser.Controller.Entities return sortable; } + [IgnoreDataMember] public Guid ParentId { get; set; } /// <summary> @@ -502,15 +510,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public Folder Parent { - get - { - if (ParentId != Guid.Empty) - { - return LibraryManager.GetItemById(ParentId) as Folder; - } - - return null; - } + get { return GetParent() as Folder; } set { @@ -525,16 +525,28 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public IEnumerable<Folder> Parents { - get + get { return GetParents().OfType<Folder>(); } + } + + public BaseItem GetParent() + { + if (ParentId != Guid.Empty) { - var parent = Parent; + return LibraryManager.GetItemById(ParentId); + } - while (parent != null) - { - yield return parent; + return null; + } - parent = parent.Parent; - } + public IEnumerable<BaseItem> GetParents() + { + var parent = GetParent(); + + while (parent != null) + { + yield return parent; + + parent = parent.GetParent(); } } @@ -546,19 +558,20 @@ namespace MediaBrowser.Controller.Entities public T FindParent<T>() where T : Folder { - return Parents.OfType<T>().FirstOrDefault(); + return GetParents().OfType<T>().FirstOrDefault(); } [IgnoreDataMember] public virtual BaseItem DisplayParent { - get { return Parent; } + get { return GetParent(); } } /// <summary> /// When the item first debuted. For movies this could be premiere date, episodes would be first aired /// </summary> /// <value>The premiere date.</value> + [IgnoreDataMember] public DateTime? PremiereDate { get; set; } /// <summary> @@ -572,31 +585,35 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the display type of the media. /// </summary> /// <value>The display type of the media.</value> + [IgnoreDataMember] public string DisplayMediaType { get; set; } /// <summary> /// Gets or sets the official rating. /// </summary> /// <value>The official rating.</value> + [IgnoreDataMember] public string OfficialRating { get; set; } /// <summary> /// Gets or sets the official rating description. /// </summary> /// <value>The official rating description.</value> + [IgnoreDataMember] public string OfficialRatingDescription { get; set; } /// <summary> /// Gets or sets the custom rating. /// </summary> /// <value>The custom rating.</value> - //[IgnoreDataMember] + [IgnoreDataMember] public string CustomRating { get; set; } /// <summary> /// Gets or sets the overview. /// </summary> /// <value>The overview.</value> + [IgnoreDataMember] public string Overview { get; set; } /// <summary> @@ -609,37 +626,48 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the genres. /// </summary> /// <value>The genres.</value> + [IgnoreDataMember] public List<string> Genres { get; set; } /// <summary> + /// Gets or sets the tags. + /// </summary> + /// <value>The tags.</value> + public List<string> Tags { get; set; } + + /// <summary> /// Gets or sets the home page URL. /// </summary> /// <value>The home page URL.</value> + [IgnoreDataMember] public string HomePageUrl { get; set; } /// <summary> /// Gets or sets the community rating. /// </summary> /// <value>The community rating.</value> - //[IgnoreDataMember] + [IgnoreDataMember] public float? CommunityRating { get; set; } /// <summary> /// Gets or sets the community rating vote count. /// </summary> /// <value>The community rating vote count.</value> + [IgnoreDataMember] public int? VoteCount { get; set; } /// <summary> /// Gets or sets the run time ticks. /// </summary> /// <value>The run time ticks.</value> + [IgnoreDataMember] public long? RunTimeTicks { get; set; } /// <summary> /// Gets or sets the production year. /// </summary> /// <value>The production year.</value> + [IgnoreDataMember] public int? ProductionYear { get; set; } /// <summary> @@ -647,19 +675,34 @@ namespace MediaBrowser.Controller.Entities /// This could be episode number, album track number, etc. /// </summary> /// <value>The index number.</value> - //[IgnoreDataMember] + [IgnoreDataMember] public int? IndexNumber { get; set; } /// <summary> /// For an episode this could be the season number, or for a song this could be the disc number. /// </summary> /// <value>The parent index number.</value> + [IgnoreDataMember] public int? ParentIndexNumber { get; set; } [IgnoreDataMember] - public virtual string OfficialRatingForComparison + public string OfficialRatingForComparison { - get { return OfficialRating; } + get + { + if (!string.IsNullOrWhiteSpace(OfficialRating)) + { + return OfficialRating; + } + + var parent = DisplayParent; + if (parent != null) + { + return parent.OfficialRatingForComparison; + } + + return null; + } } [IgnoreDataMember] @@ -721,21 +764,21 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths(files, directoryService, null) .OfType<Audio.Audio>() .Select(audio => - { - // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio; - - if (dbItem != null) { - audio = dbItem; - } + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio; - audio.ExtraType = ExtraType.ThemeSong; + if (dbItem != null) + { + audio = dbItem; + } - return audio; + audio.ExtraType = ExtraType.ThemeSong; - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); + return audio; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); } /// <summary> @@ -751,21 +794,21 @@ namespace MediaBrowser.Controller.Entities return LibraryManager.ResolvePaths(files, directoryService, null) .OfType<Video>() .Select(item => - { - // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.GetItemById(item.Id) as Video; - - if (dbItem != null) { - item = dbItem; - } + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(item.Id) as Video; + + if (dbItem != null) + { + item = dbItem; + } - item.ExtraType = ExtraType.ThemeVideo; + item.ExtraType = ExtraType.ThemeVideo; - return item; + return item; - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); } public Task RefreshMetadata(CancellationToken cancellationToken) @@ -821,7 +864,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] protected virtual bool SupportsOwnedItems { - get { return IsFolder || Parent != null; } + get { return IsFolder || GetParent() != null; } } [IgnoreDataMember] @@ -846,7 +889,7 @@ namespace MediaBrowser.Controller.Entities var localTrailersChanged = false; - if (LocationType == LocationType.FileSystem && Parent != null) + if (LocationType == LocationType.FileSystem && GetParent() != null) { var hasThemeMedia = this as IHasThemeMedia; if (hasThemeMedia != null) @@ -1008,7 +1051,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(lang)) { - lang = Parents + lang = GetParents() .Select(i => i.PreferredMetadataLanguage) .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } @@ -1038,7 +1081,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrWhiteSpace(lang)) { - lang = Parents + lang = GetParents() .Select(i => i.PreferredMetadataCountryCode) .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); } @@ -1120,6 +1163,23 @@ namespace MediaBrowser.Controller.Entities public int? GetParentalRatingValue() { + var rating = CustomRating; + + if (string.IsNullOrWhiteSpace(rating)) + { + rating = OfficialRating; + } + + if (string.IsNullOrWhiteSpace(rating)) + { + return null; + } + + return LocalizationManager.GetRatingLevel(rating); + } + + public int? GetInheritedParentalRatingValue() + { var rating = CustomRatingForComparison; if (string.IsNullOrWhiteSpace(rating)) @@ -1156,6 +1216,11 @@ namespace MediaBrowser.Controller.Entities return true; } + public virtual UnratedItem GetBlockUnratedType() + { + return UnratedItem.Other; + } + /// <summary> /// Gets the block unrated value. /// </summary> @@ -1174,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities return false; } - return config.BlockUnratedItems.Contains(UnratedItem.Other); + return config.BlockUnratedItems.Contains(GetBlockUnratedType()); } /// <summary> @@ -1206,14 +1271,14 @@ namespace MediaBrowser.Controller.Entities return false; } - if (Parents.Any(i => !i.IsVisible(user))) + if (GetParents().Any(i => !i.IsVisible(user))) { return false; } if (checkFolders) { - var topParent = Parents.LastOrDefault() ?? this; + var topParent = GetParents().LastOrDefault() ?? this; if (string.IsNullOrWhiteSpace(topParent.Path)) { @@ -1308,15 +1373,6 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Adds a person to the item - /// </summary> - /// <param name="person">The person.</param> - /// <exception cref="System.ArgumentNullException"></exception> - public void AddPerson(PersonInfo person) - { - } - - /// <summary> /// Adds a studio to the item /// </summary> /// <param name="name">The name.</param> @@ -1779,7 +1835,8 @@ namespace MediaBrowser.Controller.Entities ProviderIds = ProviderIds, IndexNumber = IndexNumber, ParentIndexNumber = ParentIndexNumber, - Year = ProductionYear + Year = ProductionYear, + PremiereDate = PremiereDate }; } @@ -1874,5 +1931,54 @@ namespace MediaBrowser.Controller.Entities DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture) }; } + + public virtual IEnumerable<Guid> GetAncestorIds() + { + return GetParents().Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id)); + } + + public BaseItem GetTopParent() + { + if (IsTopParent) + { + return this; + } + + return GetParents().FirstOrDefault(i => i.IsTopParent); + } + + [IgnoreDataMember] + public virtual bool IsTopParent + { + get + { + if (GetParent() is AggregateFolder || this is Channel || this is BasePluginFolder) + { + return true; + } + + var view = this as UserView; + if (view != null && string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsAncestors + { + get + { + return true; + } + } + + public virtual IEnumerable<Guid> GetIdsForAncestorQuery() + { + return new[] { Id }; + } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/Book.cs b/MediaBrowser.Controller/Entities/Book.cs index d31675baf..f006fedd2 100644 --- a/MediaBrowser.Controller/Entities/Book.cs +++ b/MediaBrowser.Controller/Entities/Book.cs @@ -17,19 +17,8 @@ namespace MediaBrowser.Controller.Entities } } - /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } - public string SeriesName { get; set; } - public Book() - { - Tags = new List<string>(); - } - public override bool CanDownload() { var locationType = LocationType; @@ -37,9 +26,9 @@ namespace MediaBrowser.Controller.Entities locationType != LocationType.Virtual; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Book); + return UnratedItem.Book; } public BookInfo GetLookupInfo() @@ -48,7 +37,7 @@ namespace MediaBrowser.Controller.Entities if (string.IsNullOrEmpty(SeriesName)) { - info.SeriesName = Parents.Select(i => i.Name).FirstOrDefault(); + info.SeriesName = GetParents().Select(i => i.Name).FirstOrDefault(); } else { diff --git a/MediaBrowser.Controller/Entities/CollectionFolder.cs b/MediaBrowser.Controller/Entities/CollectionFolder.cs index 0da253186..b2c7c2fa8 100644 --- a/MediaBrowser.Controller/Entities/CollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/CollectionFolder.cs @@ -181,9 +181,7 @@ namespace MediaBrowser.Controller.Entities } private List<LinkedChild> GetLinkedChildrenInternal() { - return LibraryManager.RootFolder.Children - .OfType<Folder>() - .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)) + return GetPhysicalParents() .SelectMany(c => c.LinkedChildren) .ToList(); } @@ -199,11 +197,14 @@ namespace MediaBrowser.Controller.Entities private IEnumerable<BaseItem> GetActualChildren() { - return - LibraryManager.RootFolder.Children + return GetPhysicalParents().SelectMany(c => c.Children); + } + + public IEnumerable<Folder> GetPhysicalParents() + { + return LibraryManager.RootFolder.Children .OfType<Folder>() - .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)) - .SelectMany(c => c.Children); + .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase)); } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 9ea1b64c0..824a067ad 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -28,7 +28,6 @@ namespace MediaBrowser.Controller.Entities public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } - public List<string> Tags { get; set; } public Folder() { @@ -36,7 +35,6 @@ namespace MediaBrowser.Controller.Entities ThemeSongIds = new List<Guid>(); ThemeVideoIds = new List<Guid>(); - Tags = new List<string>(); } [IgnoreDataMember] @@ -151,7 +149,15 @@ namespace MediaBrowser.Controller.Entities await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); - await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); + if (!EnableNewFolderQuerying()) + { + await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); + } + } + + private static bool EnableNewFolderQuerying() + { + return ConfigurationManager.Configuration.MigrationVersion >= 1; } protected void AddChildrenInternal(IEnumerable<BaseItem> children) @@ -196,21 +202,6 @@ namespace MediaBrowser.Controller.Entities } } - [IgnoreDataMember] - public override string OfficialRatingForComparison - { - get - { - // Never want folders to be blocked by "BlockNotRated" - if (this is Series) - { - return base.OfficialRatingForComparison; - } - - return !string.IsNullOrWhiteSpace(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None"; - } - } - /// <summary> /// Removes the child. /// </summary> @@ -224,7 +215,12 @@ namespace MediaBrowser.Controller.Entities item.SetParent(null); - return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken); + if (!EnableNewFolderQuerying()) + { + return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken); + } + + return Task.FromResult(true); } /// <summary> @@ -457,32 +453,25 @@ namespace MediaBrowser.Controller.Entities { BaseItem currentChild; - if (currentChildren.TryGetValue(child.Id, out currentChild)) + if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { - if (IsValidFromResolver(currentChild, child)) + var currentChildLocationType = currentChild.LocationType; + if (currentChildLocationType != LocationType.Remote && + currentChildLocationType != LocationType.Virtual) { - var currentChildLocationType = currentChild.LocationType; - if (currentChildLocationType != LocationType.Remote && - currentChildLocationType != LocationType.Virtual) - { - currentChild.DateModified = child.DateModified; - } - - await UpdateIsOffline(currentChild, false).ConfigureAwait(false); - validChildren.Add(currentChild); - } - else - { - newItems.Add(child); - validChildren.Add(child); + currentChild.DateModified = child.DateModified; } + + await UpdateIsOffline(currentChild, false).ConfigureAwait(false); + validChildren.Add(currentChild); + + continue; } - else - { - // Brand new item - needs to be added - newItems.Add(child); - validChildren.Add(child); - } + + // Brand new item - needs to be added + child.SetParent(this); + newItems.Add(child); + validChildren.Add(child); } // If any items were added or removed.... @@ -508,7 +497,6 @@ namespace MediaBrowser.Controller.Entities } else { - await UpdateIsOffline(item, false).ConfigureAwait(false); actualRemovals.Add(item); } } @@ -519,6 +507,11 @@ namespace MediaBrowser.Controller.Entities foreach (var item in actualRemovals) { + Logger.Debug("Removed item: " + item.Path); + + item.SetParent(null); + item.IsOffline = false; + await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false); LibraryManager.ReportItemRemoved(item); } } @@ -527,7 +520,10 @@ namespace MediaBrowser.Controller.Entities AddChildrenInternal(newItems); - await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); + if (!EnableNewFolderQuerying()) + { + await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); + } } } @@ -721,7 +717,7 @@ namespace MediaBrowser.Controller.Entities return true; } - return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath); + return false; } /// <summary> @@ -757,19 +753,16 @@ namespace MediaBrowser.Controller.Entities /// <returns>IEnumerable{BaseItem}.</returns> protected IEnumerable<BaseItem> GetCachedChildren() { - if (ConfigurationManager.Configuration.DisableStartupScan) + if (EnableNewFolderQuerying()) { - return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); - //return ItemRepository.GetItems(new InternalItemsQuery - //{ - // ParentId = Id + return ItemRepository.GetItemList(new InternalItemsQuery + { + ParentId = Id - //}).Items.Select(RetrieveChild).Where(i => i != null); - } - else - { - return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); + }).Select(RetrieveChild).Where(i => i != null); } + + return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); } private BaseItem RetrieveChild(BaseItem child) @@ -832,20 +825,8 @@ namespace MediaBrowser.Controller.Entities return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager); } - /// <summary> - /// Gets allowed children of an item - /// </summary> - /// <param name="user">The user.</param> - /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> - /// <returns>IEnumerable{BaseItem}.</returns> - /// <exception cref="System.ArgumentNullException"></exception> public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) { - return GetChildren(user, includeLinkedChildren, false); - } - - internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden) - { if (user == null) { throw new ArgumentNullException(); @@ -856,7 +837,7 @@ namespace MediaBrowser.Controller.Entities var result = new Dictionary<Guid, BaseItem>(); - AddChildren(user, includeLinkedChildren, result, includeHidden, false, null); + AddChildren(user, includeLinkedChildren, result, false, null); return result.Values; } @@ -872,29 +853,25 @@ namespace MediaBrowser.Controller.Entities /// <param name="user">The user.</param> /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> /// <param name="result">The result.</param> - /// <param name="includeHidden">if set to <c>true</c> [include hidden].</param> /// <param name="recursive">if set to <c>true</c> [recursive].</param> /// <param name="filter">The filter.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter) + private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter) { foreach (var child in GetEligibleChildrenForRecursiveChildren(user)) { if (child.IsVisible(user)) { - if (includeHidden || !child.IsHiddenFromUser(user)) + if (filter == null || filter(child)) { - if (filter == null || filter(child)) - { - result[child.Id] = child; - } + result[child.Id] = child; } if (recursive && child.IsFolder) { var folder = (Folder)child; - folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter); + folder.AddChildren(user, includeLinkedChildren, result, true, filter); } } } @@ -935,7 +912,7 @@ namespace MediaBrowser.Controller.Entities var result = new Dictionary<Guid, BaseItem>(); - AddChildren(user, true, result, false, true, filter); + AddChildren(user, true, result, true, filter); return result.Values; } @@ -1184,6 +1161,7 @@ namespace MediaBrowser.Controller.Entities Recursive = true, IsFolder = false, IsUnaired = false + }; if (!user.Configuration.DisplayMissingEpisodes) @@ -1322,4 +1300,4 @@ namespace MediaBrowser.Controller.Entities } } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Controller/Entities/Game.cs b/MediaBrowser.Controller/Entities/Game.cs index ed3e85d58..e073d09f6 100644 --- a/MediaBrowser.Controller/Entities/Game.cs +++ b/MediaBrowser.Controller/Entities/Game.cs @@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities RemoteTrailerIds = new List<Guid>(); ThemeSongIds = new List<Guid>(); ThemeVideoIds = new List<Guid>(); - Tags = new List<string>(); } public List<Guid> LocalTrailerIds { get; set; } @@ -35,12 +34,6 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } - - /// <summary> /// Gets or sets the remote trailers. /// </summary> /// <value>The remote trailers.</value> @@ -105,9 +98,9 @@ namespace MediaBrowser.Controller.Entities return base.GetDeletePaths(); } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Game); + return UnratedItem.Game; } public GameInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/GameSystem.cs b/MediaBrowser.Controller/Entities/GameSystem.cs index 35f7e3350..bc35c4738 100644 --- a/MediaBrowser.Controller/Entities/GameSystem.cs +++ b/MediaBrowser.Controller/Entities/GameSystem.cs @@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.Entities return false; } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Game; + } + public GameSystemInfo GetLookupInfo() { var id = GetItemLookupInfo<GameSystemInfo>(); diff --git a/MediaBrowser.Controller/Entities/ICollectionFolder.cs b/MediaBrowser.Controller/Entities/ICollectionFolder.cs index b55ca0a17..f4544f173 100644 --- a/MediaBrowser.Controller/Entities/ICollectionFolder.cs +++ b/MediaBrowser.Controller/Entities/ICollectionFolder.cs @@ -16,6 +16,11 @@ namespace MediaBrowser.Controller.Entities IEnumerable<string> PhysicalLocations { get; } } + public interface ISupportsUserSpecificView + { + bool EnableUserSpecificView { get; } + } + public static class CollectionFolderExtensions { public static string GetViewType(this ICollectionFolder folder, User user) diff --git a/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs b/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs new file mode 100644 index 000000000..82d581fcf --- /dev/null +++ b/MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Entities +{ + public interface IHiddenFromDisplay + { + /// <summary> + /// Determines whether the specified user is hidden. + /// </summary> + /// <param name="user">The user.</param> + /// <returns><c>true</c> if the specified user is hidden; otherwise, <c>false</c>.</returns> + bool IsHiddenFromUser(User user); + } +} diff --git a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs index 785e2fd2b..96cd305a6 100644 --- a/MediaBrowser.Controller/Entities/InternalItemsQuery.cs +++ b/MediaBrowser.Controller/Entities/InternalItemsQuery.cs @@ -1,6 +1,7 @@ using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Entities { @@ -30,6 +31,7 @@ namespace MediaBrowser.Controller.Entities public string[] MediaTypes { get; set; } public string[] IncludeItemTypes { get; set; } public string[] ExcludeItemTypes { get; set; } + public string[] ExcludeTags { get; set; } public string[] Genres { get; set; } public bool? IsMissing { get; set; } @@ -69,12 +71,15 @@ namespace MediaBrowser.Controller.Entities public string[] Studios { get; set; } public string[] StudioIds { get; set; } + public string[] GenreIds { get; set; } public ImageType[] ImageTypes { get; set; } public VideoType[] VideoTypes { get; set; } + public UnratedItem[] BlockUnratedItems { get; set; } public int[] Years { get; set; } public string[] Tags { get; set; } public string[] OfficialRatings { get; set; } + public DateTime? MinPremiereDate { get; set; } public DateTime? MinStartDate { get; set; } public DateTime? MaxStartDate { get; set; } public DateTime? MinEndDate { get; set; } @@ -87,6 +92,7 @@ namespace MediaBrowser.Controller.Entities public int? MinPlayers { get; set; } public int? MaxPlayers { get; set; } + public int? MinIndexNumber { get; set; } public double? MinCriticRating { get; set; } public double? MinCommunityRating { get; set; } @@ -101,9 +107,14 @@ namespace MediaBrowser.Controller.Entities public LocationType? LocationType { get; set; } public Guid? ParentId { get; set; } + public string[] AncestorIds { get; set; } + public string[] TopParentIds { get; set; } + + public LocationType[] ExcludeLocationTypes { get; set; } public InternalItemsQuery() { + BlockUnratedItems = new UnratedItem[] { }; Tags = new string[] { }; OfficialRatings = new string[] { }; SortBy = new string[] { }; @@ -113,6 +124,7 @@ namespace MediaBrowser.Controller.Entities Genres = new string[] { }; Studios = new string[] { }; StudioIds = new string[] { }; + GenreIds = new string[] { }; ImageTypes = new ImageType[] { }; VideoTypes = new VideoType[] { }; Years = new int[] { }; @@ -120,6 +132,29 @@ namespace MediaBrowser.Controller.Entities PersonIds = new string[] { }; ChannelIds = new string[] { }; ItemIds = new string[] { }; + AncestorIds = new string[] { }; + TopParentIds = new string[] { }; + ExcludeTags = new string[] { }; + ExcludeLocationTypes = new LocationType[] { }; + } + + public InternalItemsQuery(User user) + : this() + { + if (user != null) + { + var policy = user.Policy; + MaxParentalRating = policy.MaxParentalRating; + + if (policy.MaxParentalRating.HasValue) + { + BlockUnratedItems = policy.BlockUnratedItems; + } + + ExcludeTags = policy.BlockedTags; + + User = user; + } } } } diff --git a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs index 9317f688f..cd3e07ea3 100644 --- a/MediaBrowser.Controller/Entities/Movies/BoxSet.cs +++ b/MediaBrowser.Controller/Entities/Movies/BoxSet.cs @@ -8,15 +8,13 @@ using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; -using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities.Movies { /// <summary> /// Class BoxSet /// </summary> - public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares + public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares { public List<Share> Shares { get; set; } @@ -65,6 +63,11 @@ namespace MediaBrowser.Controller.Entities.Movies return config.BlockUnratedItems.Contains(UnratedItem.Movie); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Movie; + } + [IgnoreDataMember] public override bool IsPreSorted { @@ -154,34 +157,6 @@ namespace MediaBrowser.Controller.Entities.Movies return GetItemLookupInfo<BoxSetInfo>(); } - public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) - { - // Refresh bottom up, children first, then the boxset - // By then hopefully the movies within will have Tmdb collection values - var items = GetRecursiveChildren().ToList(); - - var totalItems = items.Count; - var numComplete = 0; - - // Refresh songs - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); - - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - numComplete++; - double percent = numComplete; - percent /= totalItems; - progress.Report(percent * 100); - } - - // Refresh current item - await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } - public override bool IsVisible(User user) { var userId = user.Id.ToString("N"); diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 1a8148edf..749d562ac 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -130,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.Movies // Must have a parent to have special features // In other words, it must be part of the Parent/Child tree - if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder) + if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder) { var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false); @@ -159,9 +159,9 @@ namespace MediaBrowser.Controller.Entities.Movies return itemsChanged; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Movie); + return UnratedItem.Movie; } public MovieInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/MusicVideo.cs b/MediaBrowser.Controller/Entities/MusicVideo.cs index b2cad02de..8a820b5ff 100644 --- a/MediaBrowser.Controller/Entities/MusicVideo.cs +++ b/MediaBrowser.Controller/Entities/MusicVideo.cs @@ -56,9 +56,9 @@ namespace MediaBrowser.Controller.Entities return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey(); } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Music); + return UnratedItem.Music; } public MusicVideoInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index 6c277da56..120a376d4 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -101,6 +101,15 @@ namespace MediaBrowser.Controller.Entities return false; } } + + [IgnoreDataMember] + public override bool SupportsAncestors + { + get + { + return false; + } + } } /// <summary> diff --git a/MediaBrowser.Controller/Entities/Photo.cs b/MediaBrowser.Controller/Entities/Photo.cs index a3d892181..308e61590 100644 --- a/MediaBrowser.Controller/Entities/Photo.cs +++ b/MediaBrowser.Controller/Entities/Photo.cs @@ -9,12 +9,10 @@ namespace MediaBrowser.Controller.Entities { public class Photo : BaseItem, IHasTags, IHasTaglines { - public List<string> Tags { get; set; } public List<string> Taglines { get; set; } public Photo() { - Tags = new List<string>(); Taglines = new List<string>(); } @@ -51,7 +49,7 @@ namespace MediaBrowser.Controller.Entities { get { - return Parents.OfType<PhotoAlbum>().FirstOrDefault(); + return GetParents().OfType<PhotoAlbum>().FirstOrDefault(); } } @@ -70,10 +68,5 @@ namespace MediaBrowser.Controller.Entities public double? Longitude { get; set; } public double? Altitude { get; set; } public int? IsoSpeedRating { get; set; } - - protected override bool GetBlockUnratedValue(UserPolicy config) - { - return config.BlockUnratedItems.Contains(UnratedItem.Other); - } } } diff --git a/MediaBrowser.Controller/Entities/PhotoAlbum.cs b/MediaBrowser.Controller/Entities/PhotoAlbum.cs index 5b48a70e9..1f4faaf49 100644 --- a/MediaBrowser.Controller/Entities/PhotoAlbum.cs +++ b/MediaBrowser.Controller/Entities/PhotoAlbum.cs @@ -9,8 +9,9 @@ using System.Threading.Tasks; namespace MediaBrowser.Controller.Entities { - public class PhotoAlbum : Folder, IMetadataContainer + public class PhotoAlbum : Folder { + [IgnoreDataMember] public override bool SupportsLocalMetadata { get @@ -32,31 +33,5 @@ namespace MediaBrowser.Controller.Entities { return config.BlockUnratedItems.Contains(UnratedItem.Other); } - - public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken) - { - var items = GetRecursiveChildren().ToList(); - - var totalItems = items.Count; - var numComplete = 0; - - // Refresh songs - foreach (var item in items) - { - cancellationToken.ThrowIfCancellationRequested(); - - await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - numComplete++; - double percent = numComplete; - percent /= totalItems; - progress.Report(percent * 100); - } - - // Refresh current item - await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); - - progress.Report(100); - } } } diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index 822f305ed..a55527f37 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -10,13 +10,6 @@ namespace MediaBrowser.Controller.Entities /// </summary> public class Studio : BaseItem, IItemByName, IHasTags { - public List<string> Tags { get; set; } - - public Studio() - { - Tags = new List<string>(); - } - /// <summary> /// Gets the user data key. /// </summary> diff --git a/MediaBrowser.Controller/Entities/TV/Episode.cs b/MediaBrowser.Controller/Entities/TV/Episode.cs index 92ca9e970..d4f829917 100644 --- a/MediaBrowser.Controller/Entities/TV/Episode.cs +++ b/MediaBrowser.Controller/Entities/TV/Episode.cs @@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Entities.TV { get { - return Season ?? Parent; + return Season ?? GetParent(); } } @@ -116,19 +116,6 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// Our rating comes from our series - /// </summary> - [IgnoreDataMember] - public override string OfficialRatingForComparison - { - get - { - var series = Series; - return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison; - } - } - - /// <summary> /// This Episode's Series Instance /// </summary> /// <value>The series.</value> @@ -265,14 +252,28 @@ namespace MediaBrowser.Controller.Entities.TV } } + public override IEnumerable<Guid> GetAncestorIds() + { + var list = base.GetAncestorIds().ToList(); + + var seasonId = SeasonId; + + if (seasonId.HasValue && !list.Contains(seasonId.Value)) + { + list.Add(seasonId.Value); + } + + return list; + } + public override IEnumerable<string> GetDeletePaths() { return new[] { Path }; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Series); + return UnratedItem.Series; } public EpisodeInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/TV/Season.cs b/MediaBrowser.Controller/Entities/TV/Season.cs index 21b89d7a9..93eac058d 100644 --- a/MediaBrowser.Controller/Entities/TV/Season.cs +++ b/MediaBrowser.Controller/Entities/TV/Season.cs @@ -6,6 +6,7 @@ using MoreLinq; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Entities.TV { @@ -32,7 +33,7 @@ namespace MediaBrowser.Controller.Entities.TV [IgnoreDataMember] public override BaseItem DisplayParent { - get { return Series ?? Parent; } + get { return Series ?? GetParent(); } } // Genre, Rating and Stuido will all be the same @@ -88,19 +89,6 @@ namespace MediaBrowser.Controller.Entities.TV } /// <summary> - /// Our rating comes from our series - /// </summary> - [IgnoreDataMember] - public override string OfficialRatingForComparison - { - get - { - var series = Series; - return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison; - } - } - - /// <summary> /// Creates the name of the sort. /// </summary> /// <returns>System.String.</returns> @@ -234,6 +222,11 @@ namespace MediaBrowser.Controller.Entities.TV return false; } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Series; + } + [IgnoreDataMember] public string SeriesName { diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs index b23833845..420b3c313 100644 --- a/MediaBrowser.Controller/Entities/TV/Series.cs +++ b/MediaBrowser.Controller/Entities/TV/Series.cs @@ -333,6 +333,11 @@ namespace MediaBrowser.Controller.Entities.TV return config.BlockUnratedItems.Contains(UnratedItem.Series); } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.Series; + } + public SeriesInfo GetLookupInfo() { var info = GetItemLookupInfo<SeriesInfo>(); diff --git a/MediaBrowser.Controller/Entities/Trailer.cs b/MediaBrowser.Controller/Entities/Trailer.cs index 6ec512783..3c7d39e0d 100644 --- a/MediaBrowser.Controller/Entities/Trailer.cs +++ b/MediaBrowser.Controller/Entities/Trailer.cs @@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Entities get { // Local trailers are not part of children - return Parent == null; + return GetParent() == null; } } @@ -97,9 +97,9 @@ namespace MediaBrowser.Controller.Entities return base.CreateUserDataKey(); } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.Trailer); + return UnratedItem.Trailer; } public TrailerInfo GetLookupInfo() diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index a9e314ede..976c6827f 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities { public static IUserManager UserManager { get; set; } public static IXmlSerializer XmlSerializer { get; set; } - public bool EnableUserViews { get; set; } /// <summary> /// From now on all user paths will be Id-based. @@ -58,6 +57,26 @@ namespace MediaBrowser.Controller.Entities } } + private string _name; + /// <summary> + /// Gets or sets the name. + /// </summary> + /// <value>The name.</value> + public override string Name + { + get + { + return _name; + } + set + { + _name = value; + + // lazy load this again + SortName = null; + } + } + /// <summary> /// Returns the folder containing the item. /// If the item is a folder, it returns the folder itself diff --git a/MediaBrowser.Controller/Entities/UserRootFolder.cs b/MediaBrowser.Controller/Entities/UserRootFolder.cs index a78beb645..b7946cb92 100644 --- a/MediaBrowser.Controller/Entities/UserRootFolder.cs +++ b/MediaBrowser.Controller/Entities/UserRootFolder.cs @@ -55,13 +55,21 @@ namespace MediaBrowser.Controller.Entities } } + protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user) + { + var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList(); + list.AddRange(LibraryManager.RootFolder.VirtualChildren); + + return list; + } + /// <summary> /// Get the children of this folder from the actual file system /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService) { - return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren); + return base.GetNonCachedChildren(directoryService); } public override bool BeforeMetadataRefresh() diff --git a/MediaBrowser.Controller/Entities/UserView.cs b/MediaBrowser.Controller/Entities/UserView.cs index 5ee49ae5a..41c19f11d 100644 --- a/MediaBrowser.Controller/Entities/UserView.cs +++ b/MediaBrowser.Controller/Entities/UserView.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; using System.Threading.Tasks; +using System.Linq; namespace MediaBrowser.Controller.Entities { @@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.Entities public Guid DisplayParentId { get; set; } public Guid? UserId { get; set; } - + public static ITVSeriesManager TVSeriesManager; public static IPlaylistManager PlaylistManager; @@ -24,7 +25,26 @@ namespace MediaBrowser.Controller.Entities { return true; } - + + public override IEnumerable<Guid> GetIdsForAncestorQuery() + { + var list = new List<Guid>(); + + if (DisplayParentId != Guid.Empty) + { + list.Add(DisplayParentId); + } + else if (ParentId != Guid.Empty) + { + list.Add(ParentId); + } + else + { + list.Add(Id); + } + return list; + } + public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) { var parent = this as Folder; @@ -81,16 +101,11 @@ namespace MediaBrowser.Controller.Entities return GetChildren(user, false); } - public static bool IsExcludedFromGrouping(Folder folder) + public static bool IsUserSpecific(Folder folder) { var standaloneTypes = new List<string> { - CollectionType.Books, - CollectionType.HomeVideos, - CollectionType.Photos, - CollectionType.Playlists, - CollectionType.BoxSets, - CollectionType.MusicVideos + CollectionType.Playlists }; var collectionFolder = folder as ICollectionFolder; @@ -100,25 +115,63 @@ namespace MediaBrowser.Controller.Entities return false; } + var supportsUserSpecific = folder as ISupportsUserSpecificView; + if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView) + { + return true; + } + return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); } - public static bool IsUserSpecific(Folder folder) + public static bool IsEligibleForGrouping(Folder folder) { - var standaloneTypes = new List<string> - { - CollectionType.Playlists, - CollectionType.BoxSets + var collectionFolder = folder as ICollectionFolder; + return collectionFolder != null && IsEligibleForGrouping(collectionFolder.CollectionType); + } + + public static bool IsEligibleForGrouping(string viewType) + { + var types = new[] + { + CollectionType.Movies, + CollectionType.TvShows, + string.Empty }; - var collectionFolder = folder as ICollectionFolder; + return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + } - if (collectionFolder == null) - { - return false; - } + public static bool IsEligibleForEnhancedView(string viewType) + { + var types = new[] + { + CollectionType.Movies, + CollectionType.TvShows + }; - return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty); + return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + } + + public static bool EnableOriginalFolder(string viewType) + { + var types = new[] + { + CollectionType.Games, + CollectionType.Books, + CollectionType.MusicVideos, + CollectionType.HomeVideos, + CollectionType.Photos, + CollectionType.Music, + CollectionType.BoxSets + }; + + return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); + } + + protected override Task ValidateChildrenInternal(IProgress<double> progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService) + { + return Task.FromResult(true); } [IgnoreDataMember] diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index f5800ce81..59ed67d9c 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -120,59 +120,34 @@ namespace MediaBrowser.Controller.Entities return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false); } + case CollectionType.Photos: case CollectionType.Books: case CollectionType.HomeVideos: + case CollectionType.Games: case CollectionType.MusicVideos: + { + if (query.Recursive) + { + return GetResult(queryParent.GetRecursiveChildren(user, true), queryParent, query); + } return GetResult(queryParent.GetChildren(user, true), queryParent, query); + } case CollectionType.Folders: return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query); - case CollectionType.Games: - return await GetGameView(user, queryParent, query).ConfigureAwait(false); - case CollectionType.Playlists: return await GetPlaylistsView(queryParent, user, query).ConfigureAwait(false); case CollectionType.BoxSets: return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false); - case CollectionType.Photos: - return await GetPhotosView(queryParent, user, query).ConfigureAwait(false); - case CollectionType.TvShows: return await GetTvView(queryParent, user, query).ConfigureAwait(false); - case CollectionType.Music: - return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false); - case CollectionType.Movies: return await GetMovieFolders(queryParent, user, query).ConfigureAwait(false); - case SpecialFolder.MusicGenres: - return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false); - - case SpecialFolder.MusicGenre: - return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false); - - case SpecialFolder.GameGenres: - return await GetGameGenres(queryParent, user, query).ConfigureAwait(false); - - case SpecialFolder.GameGenre: - return await GetGameGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false); - - case SpecialFolder.GameSystems: - return GetGameSystems(queryParent, user, query); - - case SpecialFolder.LatestGames: - return GetLatestGames(queryParent, user, query); - - case SpecialFolder.RecentlyPlayedGames: - return GetRecentlyPlayedGames(queryParent, user, query); - - case SpecialFolder.GameFavorites: - return GetFavoriteGames(queryParent, user, query); - case SpecialFolder.TvShowSeries: return GetTvSeries(queryParent, user, query); @@ -212,6 +187,21 @@ namespace MediaBrowser.Controller.Entities case SpecialFolder.MovieCollections: return GetMovieCollections(queryParent, user, query); + case SpecialFolder.TvFavoriteEpisodes: + return GetFavoriteEpisodes(queryParent, user, query); + + case SpecialFolder.TvFavoriteSeries: + return GetFavoriteSeries(queryParent, user, query); + + case CollectionType.Music: + return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false); + + case SpecialFolder.MusicGenres: + return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false); + + case SpecialFolder.MusicGenre: + return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false); + case SpecialFolder.MusicLatest: return GetMusicLatest(queryParent, user, query); @@ -230,12 +220,6 @@ namespace MediaBrowser.Controller.Entities case SpecialFolder.MusicSongs: return GetMusicSongs(queryParent, user, query); - case SpecialFolder.TvFavoriteEpisodes: - return GetFavoriteEpisodes(queryParent, user, query); - - case SpecialFolder.TvFavoriteSeries: - return GetFavoriteSeries(queryParent, user, query); - case SpecialFolder.MusicFavorites: return await GetMusicFavorites(queryParent, user, query).ConfigureAwait(false); @@ -262,18 +246,6 @@ namespace MediaBrowser.Controller.Entities } } - private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query) - { - var list = _playlistManager.GetPlaylists(user.Id.ToString("N")); - - return GetResult(list, parent, query); - } - - private int GetSpecialItemsLimit() - { - return 50; - } - private async Task<QueryResult<BaseItem>> GetMusicFolders(Folder parent, User user, InternalItemsQuery query) { if (query.Recursive) @@ -289,7 +261,7 @@ namespace MediaBrowser.Controller.Entities list.Add(await GetUserView(SpecialFolder.MusicPlaylists, "1", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicAlbums, "2", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, "3", parent).ConfigureAwait(false)); - //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false)); + list.Add(await GetUserView(SpecialFolder.MusicArtists, "4", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicSongs, "5", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicGenres, "6", parent).ConfigureAwait(false)); list.Add(await GetUserView(SpecialFolder.MusicFavorites, "7", parent).ConfigureAwait(false)); @@ -422,6 +394,36 @@ namespace MediaBrowser.Controller.Entities return PostFilterAndSort(items, parent, null, query); } + private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query) + { + query.IsFavorite = true; + + var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query)); + + return PostFilterAndSort(items, parent, null, query); + } + + private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query) + { + query.IsFavorite = true; + + var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query)); + + return PostFilterAndSort(items, parent, null, query); + } + + private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query) + { + var list = _playlistManager.GetPlaylists(user.Id.ToString("N")); + + return GetResult(list, parent, query); + } + + private int GetSpecialItemsLimit() + { + return 50; + } + private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query) { if (query.Recursive) @@ -480,24 +482,6 @@ namespace MediaBrowser.Controller.Entities return PostFilterAndSort(items, parent, null, query); } - private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query) - { - query.IsFavorite = true; - - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); - } - - private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query) - { - query.IsFavorite = true; - - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); - } - private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query) { var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query)); @@ -617,54 +601,6 @@ namespace MediaBrowser.Controller.Entities return GetResult(list, parent, query); } - private async Task<QueryResult<BaseItem>> GetGameView(User user, Folder parent, InternalItemsQuery query) - { - if (query.Recursive) - { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => FilterItem(i, query)); - return PostFilterAndSort(items, parent, null, query); - } - - var list = new List<BaseItem>(); - - list.Add(await GetUserView(SpecialFolder.LatestGames, "0", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, "1", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.GameFavorites, "2", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.GameSystems, "3", parent).ConfigureAwait(false)); - list.Add(await GetUserView(SpecialFolder.GameGenres, "4", parent).ConfigureAwait(false)); - - return GetResult(list, parent, query); - } - - private QueryResult<BaseItem> GetLatestGames(Folder parent, User user, InternalItemsQuery query) - { - query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; - query.SortOrder = SortOrder.Descending; - - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); - } - - private QueryResult<BaseItem> GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query) - { - query.IsPlayed = true; - query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName }; - query.SortOrder = SortOrder.Descending; - - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query); - } - - private QueryResult<BaseItem> GetFavoriteGames(Folder parent, User user, InternalItemsQuery query) - { - query.IsFavorite = true; - - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query)); - return PostFilterAndSort(items, parent, null, query); - } - private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query) { query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName }; @@ -745,49 +681,6 @@ namespace MediaBrowser.Controller.Entities return GetResult(items, queryParent, query); } - private QueryResult<BaseItem> GetGameSystems(Folder parent, User user, InternalItemsQuery query) - { - var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is GameSystem && FilterItem(i, query)); - - return PostFilterAndSort(items, parent, null, query); - } - - private async Task<QueryResult<BaseItem>> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query) - { - var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games }, - i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase)); - - return GetResult(items, queryParent, query); - } - - private async Task<QueryResult<BaseItem>> GetGameGenres(Folder parent, User user, InternalItemsQuery query) - { - var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }) - .OfType<Game>() - .SelectMany(i => i.Genres) - .DistinctNames() - .Select(i => - { - try - { - return _libraryManager.GetGameGenre(i); - } - catch - { - // Full exception logged at lower levels - _logger.Error("Error getting game genre"); - return null; - } - - }) - .Where(i => i != null) - .Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, i.SortName, parent)); - - var genres = await Task.WhenAll(tasks).ConfigureAwait(false); - - return GetResult(genres, parent, query); - } - private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result) where T : BaseItem { @@ -1061,6 +954,11 @@ namespace MediaBrowser.Controller.Entities return false; } + if (request.GenreIds.Length > 0) + { + return false; + } + if (request.VideoTypes.Length > 0) { return false; @@ -1101,10 +999,15 @@ namespace MediaBrowser.Controller.Entities return false; } + if (request.MinIndexNumber.HasValue) + { + return false; + } + return true; } - public static IEnumerable<BaseItem> FilterVirtualEpisodes( + private static IEnumerable<BaseItem> FilterVirtualEpisodes( IEnumerable<BaseItem> items, bool? isMissing, bool? isVirtualUnaired, @@ -1374,7 +1277,7 @@ namespace MediaBrowser.Controller.Entities if (query.IsInBoxSet.HasValue) { var val = query.IsInBoxSet.Value; - if (item.Parents.OfType<BoxSet>().Any() != val) + if (item.GetParents().OfType<BoxSet>().Any() != val) { return false; } @@ -1657,6 +1560,16 @@ namespace MediaBrowser.Controller.Entities return false; } + // Apply genre filter + if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id => + { + var genreItem = libraryManager.GetItemById(id); + return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase); + })) + { + return false; + } + // Apply year filter if (query.Years.Length > 0) { @@ -1779,6 +1692,16 @@ namespace MediaBrowser.Controller.Entities } } + if (query.MinIndexNumber.HasValue) + { + var val = query.MinIndexNumber.Value; + + if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val)) + { + return false; + } + } + return true; } @@ -1789,12 +1712,12 @@ namespace MediaBrowser.Controller.Entities return _libraryManager.RootFolder .Children .OfType<Folder>() - .Where(i => !UserView.IsExcludedFromGrouping(i)); + .Where(UserView.IsEligibleForGrouping); } return user.RootFolder - .GetChildren(user, true, true) + .GetChildren(user, true) .OfType<Folder>() - .Where(i => user.IsFolderGrouped(i.Id) && !UserView.IsExcludedFromGrouping(i)); + .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i)); } private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes) diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 10068eed7..197222669 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -186,12 +186,6 @@ namespace MediaBrowser.Controller.Entities public string ShortcutPath { get; set; } /// <summary> - /// Gets or sets the tags. - /// </summary> - /// <value>The tags.</value> - public List<string> Tags { get; set; } - - /// <summary> /// Gets or sets the video bit rate. /// </summary> /// <value>The video bit rate.</value> @@ -356,7 +350,7 @@ namespace MediaBrowser.Controller.Entities // Must have a parent to have additional parts or alternate versions // In other words, it must be part of the Parent/Child tree // The additional parts won't have additional parts themselves - if (LocationType == LocationType.FileSystem && Parent != null) + if (LocationType == LocationType.FileSystem && GetParent() != null) { if (!IsStacked) { diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs index 1b842a5f6..fb843d191 100644 --- a/MediaBrowser.Controller/IServerApplicationHost.cs +++ b/MediaBrowser.Controller/IServerApplicationHost.cs @@ -1,6 +1,8 @@ using MediaBrowser.Common; using MediaBrowser.Model.System; using System; +using System.Collections.Generic; +using System.Net; namespace MediaBrowser.Controller { @@ -63,7 +65,7 @@ namespace MediaBrowser.Controller /// Gets the local ip address. /// </summary> /// <value>The local ip address.</value> - string LocalIpAddress { get; } + List<IPAddress> LocalIpAddresses { get; } /// <summary> /// Gets the local API URL. diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 2af53efa9..3b45d7764 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -337,7 +337,6 @@ namespace MediaBrowser.Controller.Library string parentId, string viewType, string sortName, - string uniqueId, CancellationToken cancellationToken); /// <summary> @@ -391,13 +390,11 @@ namespace MediaBrowser.Controller.Library /// <param name="parent">The parent.</param> /// <param name="viewType">Type of the view.</param> /// <param name="sortName">Name of the sort.</param> - /// <param name="uniqueId">The unique identifier.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task<UserView>.</returns> Task<UserView> GetShadowView(BaseItem parent, string viewType, string sortName, - string uniqueId, CancellationToken cancellationToken); /// <summary> @@ -543,5 +540,29 @@ namespace MediaBrowser.Controller.Library /// <param name="imageIndex">Index of the image.</param> /// <returns>Task.</returns> Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex); + + /// <summary> + /// Gets the items. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="parentIds">The parent ids.</param> + /// <returns>List<BaseItem>.</returns> + IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds); + + /// <summary> + /// Gets the items result. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="parentIds">The parent ids.</param> + /// <returns>QueryResult<BaseItem>.</returns> + QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds); + + /// <summary> + /// Ignores the file. + /// </summary> + /// <param name="file">The file.</param> + /// <param name="parent">The parent.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool IgnoreFile(FileSystemMetadata file, BaseItem parent); } }
\ No newline at end of file diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 1c0415ca1..9084ea56b 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -155,7 +155,7 @@ namespace MediaBrowser.Controller.Library // Not officially supported but in some cases we can handle it. if (item == null) { - item = parent.Parents.OfType<T>().FirstOrDefault(); + item = parent.GetParents().OfType<T>().FirstOrDefault(); } return item != null; diff --git a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs index 03c05ec69..d9834c191 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs @@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv public bool IsLive { get; set; } [IgnoreDataMember] public bool IsPremiere { get; set; } - public ProgramAudio? Audio { get; set; } /// <summary> /// Gets the user data key. @@ -106,9 +105,9 @@ namespace MediaBrowser.Controller.LiveTv } } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); + return UnratedItem.LiveTvProgram; } protected override string GetInternalMetadataPath(string basePath) @@ -140,5 +139,10 @@ namespace MediaBrowser.Controller.LiveTv return list; } + + public override bool IsVisibleStandalone(User user) + { + return IsVisible(user); + } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 89168c578..8c4ee92cd 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -22,9 +22,9 @@ namespace MediaBrowser.Controller.LiveTv return GetClientTypeName() + "-" + Name; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel); + return UnratedItem.LiveTvChannel; } /// <summary> diff --git a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs index 88bbe19b7..fcd065e79 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvProgram.cs @@ -40,10 +40,11 @@ namespace MediaBrowser.Controller.LiveTv } /// <summary> - /// Gets or sets the type of the channel. + /// Gets or sets the name. /// </summary> - /// <value>The type of the channel.</value> - public ChannelType ChannelType { get; set; } + /// <value>The name.</value> + [IgnoreDataMember] + public string ServiceName { get; set; } /// <summary> /// The start date of the program, in UTC. @@ -52,12 +53,6 @@ namespace MediaBrowser.Controller.LiveTv public DateTime StartDate { get; set; } /// <summary> - /// Gets or sets the audio. - /// </summary> - /// <value>The audio.</value> - public ProgramAudio? Audio { get; set; } - - /// <summary> /// Gets or sets a value indicating whether this instance is repeat. /// </summary> /// <value><c>true</c> if this instance is repeat; otherwise, <c>false</c>.</value> @@ -72,12 +67,6 @@ namespace MediaBrowser.Controller.LiveTv public string EpisodeTitle { get; set; } /// <summary> - /// Gets or sets the name of the service. - /// </summary> - /// <value>The name of the service.</value> - public string ServiceName { get; set; } - - /// <summary> /// Gets or sets a value indicating whether this instance is movie. /// </summary> /// <value><c>true</c> if this instance is movie; otherwise, <c>false</c>.</value> @@ -153,14 +142,14 @@ namespace MediaBrowser.Controller.LiveTv } } - [IgnoreDataMember] - public override string MediaType - { - get - { - return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio; - } - } + //[IgnoreDataMember] + //public override string MediaType + //{ + // get + // { + // return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio; + // } + //} [IgnoreDataMember] public bool IsAiring @@ -189,9 +178,9 @@ namespace MediaBrowser.Controller.LiveTv return "Program"; } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); + return UnratedItem.LiveTvProgram; } protected override string GetInternalMetadataPath(string basePath) @@ -236,5 +225,14 @@ namespace MediaBrowser.Controller.LiveTv return base.SupportsPeople; } } + + [IgnoreDataMember] + public override bool SupportsAncestors + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs index 27594317d..c58b5502b 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs @@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv public bool IsLive { get; set; } [IgnoreDataMember] public bool IsPremiere { get; set; } - public ProgramAudio? Audio { get; set; } /// <summary> /// Gets the user data key. @@ -121,9 +120,9 @@ namespace MediaBrowser.Controller.LiveTv } } - protected override bool GetBlockUnratedValue(UserPolicy config) + public override UnratedItem GetBlockUnratedType() { - return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram); + return UnratedItem.LiveTvProgram; } protected override string GetInternalMetadataPath(string basePath) @@ -155,5 +154,10 @@ namespace MediaBrowser.Controller.LiveTv return list; } + + public override bool IsVisibleStandalone(User user) + { + return IsVisible(user); + } } } diff --git a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs index 175cf162b..2d58ef67f 100644 --- a/MediaBrowser.Controller/LiveTv/RecordingGroup.cs +++ b/MediaBrowser.Controller/LiveTv/RecordingGroup.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Users; namespace MediaBrowser.Controller.LiveTv @@ -11,6 +12,11 @@ namespace MediaBrowser.Controller.LiveTv return false; } + public override UnratedItem GetBlockUnratedType() + { + return UnratedItem.LiveTvProgram; + } + public override bool SupportsLocalMetadata { get diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index bcf4de2a2..a32ffdb3b 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -51,6 +51,9 @@ <Reference Include="Interfaces.IO"> <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath> </Reference> + <Reference Include="MoreLinq"> + <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> @@ -65,9 +68,6 @@ <Reference Include="ServiceStack.Interfaces"> <HintPath>..\ThirdParty\ServiceStack\ServiceStack.Interfaces.dll</HintPath> </Reference> - <Reference Include="MoreLinq"> - <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> - </Reference> </ItemGroup> <ItemGroup> <Compile Include="..\SharedVersion.cs"> @@ -162,6 +162,7 @@ <Compile Include="Entities\IHasThemeMedia.cs" /> <Compile Include="Entities\IHasTrailers.cs" /> <Compile Include="Entities\IHasUserData.cs" /> + <Compile Include="Entities\IHiddenFromDisplay.cs" /> <Compile Include="Entities\IItemByName.cs" /> <Compile Include="Entities\ILibraryItem.cs" /> <Compile Include="Entities\ImageSourceInfo.cs" /> diff --git a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs index 24df7b885..ca0c2fdbb 100644 --- a/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs +++ b/MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs @@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.MediaEncoding public IIsoMount MountedIso { get; set; } public VideoType VideoType { get; set; } public List<string> PlayableStreamFileNames { get; set; } - public bool ExtractKeyFrameInterval { get; set; } public MediaInfoRequest() { diff --git a/MediaBrowser.Controller/Net/IHttpServer.cs b/MediaBrowser.Controller/Net/IHttpServer.cs index 91da5fab2..97c5dd31b 100644 --- a/MediaBrowser.Controller/Net/IHttpServer.cs +++ b/MediaBrowser.Controller/Net/IHttpServer.cs @@ -29,12 +29,6 @@ namespace MediaBrowser.Controller.Net void StartServer(IEnumerable<string> urlPrefixes, string certificatePath); /// <summary> - /// Gets the local end points. - /// </summary> - /// <value>The local end points.</value> - IEnumerable<string> LocalEndPoints { get; } - - /// <summary> /// Stops this instance. /// </summary> void Stop(); diff --git a/MediaBrowser.Controller/Persistence/IItemRepository.cs b/MediaBrowser.Controller/Persistence/IItemRepository.cs index 8fc0aedd3..533d66b95 100644 --- a/MediaBrowser.Controller/Persistence/IItemRepository.cs +++ b/MediaBrowser.Controller/Persistence/IItemRepository.cs @@ -176,6 +176,20 @@ namespace MediaBrowser.Controller.Persistence /// <param name="query">The query.</param> /// <returns>QueryResult<Tuple<Guid, System.String>>.</returns> QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query); + + /// <summary> + /// Gets the item list. + /// </summary> + /// <param name="query">The query.</param> + /// <returns>List<BaseItem>.</returns> + IEnumerable<BaseItem> GetItemList(InternalItemsQuery query); + + /// <summary> + /// Updates the inherited values. + /// </summary> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task.</returns> + Task UpdateInheritedValues(CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Playlists/Playlist.cs b/MediaBrowser.Controller/Playlists/Playlist.cs index fdc36db35..0f9af6550 100644 --- a/MediaBrowser.Controller/Playlists/Playlist.cs +++ b/MediaBrowser.Controller/Playlists/Playlist.cs @@ -144,6 +144,7 @@ namespace MediaBrowser.Controller.Playlists public string PlaylistMediaType { get; set; } + [IgnoreDataMember] public override string MediaType { get diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs index 91dc33214..7114cde3e 100644 --- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs @@ -33,6 +33,7 @@ namespace MediaBrowser.Controller.Providers public int? Year { get; set; } public int? IndexNumber { get; set; } public int? ParentIndexNumber { get; set; } + public DateTime? PremiereDate { get; set; } public ItemLookupInfo() { diff --git a/MediaBrowser.Controller/Providers/MetadataStatus.cs b/MediaBrowser.Controller/Providers/MetadataStatus.cs index 9b946aee2..b19377a68 100644 --- a/MediaBrowser.Controller/Providers/MetadataStatus.cs +++ b/MediaBrowser.Controller/Providers/MetadataStatus.cs @@ -11,24 +11,6 @@ namespace MediaBrowser.Controller.Providers public Guid ItemId { get; set; } /// <summary> - /// Gets or sets the name of the item. - /// </summary> - /// <value>The name of the item.</value> - public string ItemName { get; set; } - - /// <summary> - /// Gets or sets the type of the item. - /// </summary> - /// <value>The type of the item.</value> - public string ItemType { get; set; } - - /// <summary> - /// Gets or sets the name of the series. - /// </summary> - /// <value>The name of the series.</value> - public string SeriesName { get; set; } - - /// <summary> /// Gets or sets the date last metadata refresh. /// </summary> /// <value>The date last metadata refresh.</value> @@ -40,22 +22,8 @@ namespace MediaBrowser.Controller.Providers /// <value>The date last images refresh.</value> public DateTime? DateLastImagesRefresh { get; set; } - /// <summary> - /// Gets or sets the last result error message. - /// </summary> - /// <value>The last result error message.</value> - public string LastErrorMessage { get; set; } - public DateTime? ItemDateModified { get; set; } - public void AddStatus(string errorMessage) - { - if (string.IsNullOrEmpty(LastErrorMessage)) - { - LastErrorMessage = errorMessage; - } - } - public bool IsDirty { get; private set; } public void SetDateLastMetadataRefresh(DateTime? date) diff --git a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs index e85535548..2c82882c6 100644 --- a/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs +++ b/MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Controller.Library; +using CommonIO; +using MediaBrowser.Controller.Entities; namespace MediaBrowser.Controller.Resolvers { @@ -7,6 +8,6 @@ namespace MediaBrowser.Controller.Resolvers /// </summary> public interface IResolverIgnoreRule { - bool ShouldIgnore(ItemResolveArgs args); + bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent); } } diff --git a/MediaBrowser.Controller/packages.config b/MediaBrowser.Controller/packages.config index a0aacbc95..90b347929 100644 --- a/MediaBrowser.Controller/packages.config +++ b/MediaBrowser.Controller/packages.config @@ -2,6 +2,6 @@ <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> <package id="Interfaces.IO" version="1.0.0.5" 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" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs index 2ab27fde5..21289970e 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs @@ -26,6 +26,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private readonly ILocalizationManager _localization; private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; + private readonly IUserViewManager _userViewManager; public ContentDirectory(IDlnaManager dlna, IUserDataManager userDataManager, @@ -34,7 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory IServerConfigurationManager config, IUserManager userManager, ILogger logger, - IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager) + IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager) : base(logger, httpClient) { _dlna = dlna; @@ -46,6 +47,7 @@ namespace MediaBrowser.Dlna.ContentDirectory _localization = localization; _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; + _userViewManager = userViewManager; } private int SystemUpdateId @@ -86,7 +88,8 @@ namespace MediaBrowser.Dlna.ContentDirectory _config, _localization, _channelManager, - _mediaSourceManager) + _mediaSourceManager, + _userViewManager) .ProcessControlRequest(request); } diff --git a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs index a9ce5587d..fb7596387 100644 --- a/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs +++ b/MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs @@ -24,6 +24,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Model.Library; namespace MediaBrowser.Dlna.ContentDirectory { @@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private readonly IUserDataManager _userDataManager; private readonly IServerConfigurationManager _config; private readonly User _user; + private readonly IUserViewManager _userViewManager; private const string NS_DC = "http://purl.org/dc/elements/1.1/"; private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/"; @@ -47,7 +49,7 @@ namespace MediaBrowser.Dlna.ContentDirectory private readonly DeviceProfile _profile; - public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager) + public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager) : base(config, logger) { _libraryManager = libraryManager; @@ -55,6 +57,7 @@ namespace MediaBrowser.Dlna.ContentDirectory _user = user; _systemUpdateId = systemUpdateId; _channelManager = channelManager; + _userViewManager = userViewManager; _profile = profile; _config = config; @@ -450,16 +453,32 @@ namespace MediaBrowser.Dlna.ContentDirectory sortOrders.Add(ItemSortBy.SortName); } - var queryResult = await folder.GetItems(new InternalItemsQuery + QueryResult<BaseItem> queryResult; + + if (folder is UserRootFolder) { - Limit = limit, - StartIndex = startIndex, - SortBy = sortOrders.ToArray(), - SortOrder = sort.SortOrder, - User = user, - Filter = FilterUnsupportedContent + var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None) + .ConfigureAwait(false); + + queryResult = new QueryResult<BaseItem> + { + Items = views.Cast<BaseItem>().ToArray() + }; + queryResult.TotalRecordCount = queryResult.Items.Length; + } + else + { + queryResult = await folder.GetItems(new InternalItemsQuery + { + Limit = limit, + StartIndex = startIndex, + SortBy = sortOrders.ToArray(), + SortOrder = sort.SortOrder, + User = user, + Filter = FilterUnsupportedContent - }).ConfigureAwait(false); + }).ConfigureAwait(false); + } var options = _config.GetDlnaConfiguration(); @@ -481,23 +500,17 @@ namespace MediaBrowser.Dlna.ContentDirectory private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit) { - var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery + var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { - Person = person.Name - - }).Items; - - var items = itemsWithPerson - .Where(i => i is Movie || i is Series || i is IChannelItem) - .Where(i => i.IsVisibleStandalone(user)) - .ToList(); + Person = person.Name, + IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name }, + SortBy = new[] { ItemSortBy.SortName }, + Limit = limit, + StartIndex = startIndex - items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending) - .Skip(startIndex ?? 0) - .Take(limit ?? int.MaxValue) - .ToList(); + }, new string[] { }); - var serverItems = items.Select(i => new ServerItem + var serverItems = itemsResult.Items.Select(i => new ServerItem { Item = i, StubType = null @@ -506,7 +519,7 @@ namespace MediaBrowser.Dlna.ContentDirectory return new QueryResult<ServerItem> { - TotalRecordCount = serverItems.Length, + TotalRecordCount = itemsResult.TotalRecordCount, Items = serverItems }; } diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 0a5b591ec..40507db0c 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -966,7 +966,7 @@ namespace MediaBrowser.Dlna.Didl } } - item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary)); + item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary)); if (item != null) { diff --git a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs index 8c45757e7..d79ef9eaf 100644 --- a/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs +++ b/MediaBrowser.Dlna/Main/DlnaEntryPoint.cs @@ -43,19 +43,19 @@ namespace MediaBrowser.Dlna.Main private readonly List<string> _registeredServerIds = new List<string>(); private bool _dlnaServerStarted; - public DlnaEntryPoint(IServerConfigurationManager config, - ILogManager logManager, - IServerApplicationHost appHost, - INetworkManager network, - ISessionManager sessionManager, - IHttpClient httpClient, - ILibraryManager libraryManager, - IUserManager userManager, - IDlnaManager dlnaManager, - IImageProcessor imageProcessor, - IUserDataManager userDataManager, - ILocalizationManager localization, - IMediaSourceManager mediaSourceManager, + public DlnaEntryPoint(IServerConfigurationManager config, + ILogManager logManager, + IServerApplicationHost appHost, + INetworkManager network, + ISessionManager sessionManager, + IHttpClient httpClient, + ILibraryManager libraryManager, + IUserManager userManager, + IDlnaManager dlnaManager, + IImageProcessor imageProcessor, + IUserDataManager userDataManager, + ILocalizationManager localization, + IMediaSourceManager mediaSourceManager, ISsdpHandler ssdpHandler, IDeviceDiscovery deviceDiscovery) { _config = config; @@ -148,13 +148,20 @@ namespace MediaBrowser.Dlna.Main private void RegisterServerEndpoints() { - foreach (var address in _network.GetLocalIpAddresses()) + foreach (var address in _appHost.LocalIpAddresses) { - var guid = address.GetMD5(); + //if (IPAddress.IsLoopback(address)) + //{ + // // Should we allow this? + // continue; + //} + + var addressString = address.ToString(); + var guid = addressString.GetMD5(); var descriptorURI = "/dlna/" + guid.ToString("N") + "/description.xml"; - var uri = new Uri(_appHost.GetLocalApiUrl(address) + descriptorURI); + var uri = new Uri(_appHost.GetLocalApiUrl(addressString) + descriptorURI); var services = new List<string> { @@ -165,8 +172,8 @@ namespace MediaBrowser.Dlna.Main "urn:microsoft.com:service:X_MS_MediaReceiverRegistrar:1", "uuid:" + guid.ToString("N") }; - - _ssdpHandler.RegisterNotification(guid, uri, IPAddress.Parse(address), services); + + _ssdpHandler.RegisterNotification(guid, uri, address, services); _registeredServerIds.Add(guid.ToString("N")); } diff --git a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj index b8ac60ef6..db69a45c8 100644 --- a/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj +++ b/MediaBrowser.Dlna/MediaBrowser.Dlna.csproj @@ -44,6 +44,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath> </Reference> + <Reference Include="MoreLinq"> + <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> diff --git a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs index 63bb0b52a..5bfd37726 100644 --- a/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs +++ b/MediaBrowser.Dlna/Profiles/WdtvLiveProfile.cs @@ -232,6 +232,35 @@ namespace MediaBrowser.Dlna.Profiles } } }; + + SubtitleProfiles = new[] + { + new SubtitleProfile + { + Format = "srt", + Method = SubtitleDeliveryMethod.External + }, + new SubtitleProfile + { + Format = "srt", + Method = SubtitleDeliveryMethod.Embed + }, + new SubtitleProfile + { + Format = "sub", + Method = SubtitleDeliveryMethod.Embed + }, + new SubtitleProfile + { + Format = "subrip", + Method = SubtitleDeliveryMethod.Embed + }, + new SubtitleProfile + { + Format = "idx", + Method = SubtitleDeliveryMethod.Embed + } + }; } } } diff --git a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml b/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml index a537cbfcf..ca98f950b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/BubbleUPnp.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>BubbleUPnp</Name> <Identification> <ModelName>BubbleUPnp</ModelName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Default.xml b/MediaBrowser.Dlna/Profiles/Xml/Default.xml index 72cad4513..58c37a423 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Default.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Default.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Generic Device</Name> <FriendlyName>Emby</FriendlyName> <Manufacturer>Emby</Manufacturer> @@ -10,6 +10,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml index bbaf23cc5..86be2d18b 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Denon AVR.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Denon AVR</Name> <Identification> <FriendlyName>Denon:\[AVR:.*</FriendlyName> @@ -15,6 +15,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml index 5de643b02..c553f9a06 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/DirecTV HD-DVR.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>DirecTV HD-DVR</Name> <Identification> <FriendlyName>^DIRECTV.*$</FriendlyName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index bf65212b9..bb14ea309 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Dish Hopper-Joey.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Dish Hopper-Joey</Name> <Identification> <Manufacturer>Echostar Technologies LLC</Manufacturer> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml index 42a255a37..5d30d141e 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Kodi.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Kodi</Name> <Identification> <ModelName>Kodi</ModelName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml index 044baaa70..62da6dbf4 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/LG Smart TV.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>LG Smart TV</Name> <Identification> <FriendlyName>LG.*</FriendlyName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml index ca64d36d4..9d83164d8 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Linksys DMA2100.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Linksys DMA2100</Name> <Identification> <ModelName>DMA2100us</ModelName> @@ -14,6 +14,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml index 61e7fedf8..b66ebfe9e 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/MediaMonkey.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>MediaMonkey</Name> <Identification> <FriendlyName>MediaMonkey</FriendlyName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml index 147182b7e..329c97fbd 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Panasonic Viera.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Panasonic Viera</Name> <Identification> <FriendlyName>VIERA</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml index 1378341fb..a53e731cf 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Popcorn Hour.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Popcorn Hour</Name> <FriendlyName>Emby</FriendlyName> <Manufacturer>Emby</Manufacturer> @@ -10,6 +10,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml index c9f139eaa..642395ac8 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Samsung Smart TV.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Samsung Smart TV</Name> <Identification> <ModelUrl>samsung.com</ModelUrl> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml index 7eb9db7f6..afcce2b07 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player 2013.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Blu-ray Player 2013</Name> <Identification> <FriendlyName>Blu-ray Disc Player</FriendlyName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml index 013600f87..8dc221251 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Blu-ray Player.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Blu-ray Player</Name> <Identification> <FriendlyName>Blu-ray Disc Player</FriendlyName> @@ -18,6 +18,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml index 65252f749..69dae2806 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2010).xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Bravia (2010)</Name> <Identification> <FriendlyName>KDL-\d{2}[EHLNPB]X\d[01]\d.*</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://www.microsoft.com/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml index 5f7f0a2ee..2292d634d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2011).xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Bravia (2011)</Name> <Identification> <FriendlyName>KDL-\d{2}([A-Z]X\d2\d|CX400).*</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://www.microsoft.com/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml index 0951d5e0e..095fcb28e 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2012).xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Bravia (2012)</Name> <Identification> <FriendlyName>KDL-\d{2}[A-Z]X\d5(\d|G).*</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://www.microsoft.com/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml index d3a7dbab9..c700c4bfb 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2013).xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Bravia (2013)</Name> <Identification> <FriendlyName>KDL-\d{2}[WR][5689]\d{2}A.*</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://www.microsoft.com/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml index 2859743fb..96fa2861d 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony Bravia (2014).xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony Bravia (2014)</Name> <Identification> <FriendlyName>(KDL-\d{2}W[5-9]\d{2}B|KDL-\d{2}R480|XBR-\d{2}X[89]\d{2}B|KD-\d{2}[SX][89]\d{3}B).*</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://www.microsoft.com/</ModelUrl> <EnableAlbumArtInDidl>true</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml index 33066d8ef..5d6e72000 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 3.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony PlayStation 3</Name> <Identification> <FriendlyName>PLAYSTATION 3</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 3b372aec5..939c8a813 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Sony PlayStation 4.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Sony PlayStation 4</Name> <Identification> <FriendlyName>PLAYSTATION 4</FriendlyName> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>true</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_TN</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml b/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml index 6b341671c..82b806f1a 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Vlc.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Vlc</Name> <Identification> <ModelName>Vlc</ModelName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml index 7f216a65c..df2a746d0 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/WDTV Live.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>WDTV Live</Name> <Identification> <ModelName>WD TV</ModelName> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> @@ -83,5 +84,11 @@ <Conditions /> </ResponseProfile> </ResponseProfiles> - <SubtitleProfiles /> + <SubtitleProfiles> + <SubtitleProfile format="srt" method="External" /> + <SubtitleProfile format="srt" method="Embed" /> + <SubtitleProfile format="sub" method="Embed" /> + <SubtitleProfile format="subrip" method="Embed" /> + <SubtitleProfile format="idx" method="Embed" /> + </SubtitleProfiles> </Profile>
\ No newline at end of file diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml index 118224380..bfe1fceea 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox 360.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Xbox 360</Name> <Identification> <ModelName>Xbox 360</ModelName> @@ -17,6 +17,7 @@ <ModelUrl>http://go.microsoft.com/fwlink/?LinkId=105926</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml index 4c6287442..bd315ac66 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/Xbox One.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>Xbox One</Name> <Identification> <ModelName>Xbox One</ModelName> @@ -17,6 +17,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio,Photo,Video</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml index ca95ecde3..a282af655 100644 --- a/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml +++ b/MediaBrowser.Dlna/Profiles/Xml/foobar2000.xml @@ -1,5 +1,5 @@ <?xml version="1.0"?> -<Profile xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> +<Profile xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <Name>foobar2000</Name> <Identification> <FriendlyName>foobar</FriendlyName> @@ -16,6 +16,7 @@ <ModelUrl>http://emby.media/</ModelUrl> <EnableAlbumArtInDidl>false</EnableAlbumArtInDidl> <EnableSingleAlbumArtLimit>false</EnableSingleAlbumArtLimit> + <EnableSingleSubtitleLimit>false</EnableSingleSubtitleLimit> <SupportedMediaTypes>Audio</SupportedMediaTypes> <AlbumArtPn>JPEG_SM</AlbumArtPn> <MaxAlbumArtWidth>480</MaxAlbumArtWidth> diff --git a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs index c9b983bae..279979550 100644 --- a/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs +++ b/MediaBrowser.Dlna/Ssdp/DeviceDiscovery.cs @@ -11,6 +11,7 @@ using System.Net.NetworkInformation; using System.Net.Sockets; using System.Threading; using System.Threading.Tasks; +using MoreLinq; namespace MediaBrowser.Dlna.Ssdp { @@ -36,40 +37,60 @@ namespace MediaBrowser.Dlna.Ssdp _appHost = appHost; } + private List<IPAddress> GetLocalIpAddresses() + { + NetworkInterface[] interfaces; + + try + { + interfaces = NetworkInterface.GetAllNetworkInterfaces(); + } + catch (Exception ex) + { + _logger.ErrorException("Error in GetAllNetworkInterfaces", ex); + return new List<IPAddress>(); + } + + return interfaces.SelectMany(network => { + + try + { + _logger.Debug("Querying interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus); + + var properties = network.GetIPProperties(); + + return properties.UnicastAddresses + .Select(i => i.Address) + .Where(i => i.AddressFamily == AddressFamily.InterNetwork) + .ToList(); + } + catch (Exception ex) + { + _logger.ErrorException("Error querying network interface", ex); + return new List<IPAddress>(); + } + + }) + .DistinctBy(i => i.ToString()) + .ToList(); + } + public void Start(SsdpHandler ssdpHandler) { _ssdpHandler = ssdpHandler; _ssdpHandler.MessageReceived += _ssdpHandler_MessageReceived; - foreach (var network in GetNetworkInterfaces()) - { - _logger.Debug("Found interface: {0}. Type: {1}. Status: {2}", network.Name, network.NetworkInterfaceType, network.OperationalStatus); - - if (!network.SupportsMulticast || OperationalStatus.Up != network.OperationalStatus || !network.GetIPProperties().MulticastAddresses.Any()) - continue; - - var properties = network.GetIPProperties(); - var ipV4 = properties.GetIPv4Properties(); - if (null == ipV4) - continue; - - var localIps = properties.UnicastAddresses - .Where(i => i.Address.AddressFamily == AddressFamily.InterNetwork) - .Select(i => i.Address) - .ToList(); - - foreach (var localIp in localIps) - { - try - { - CreateListener(localIp); - } - catch (Exception e) - { - _logger.ErrorException("Failed to Initilize Socket", e); - } - } - } + foreach (var localIp in GetLocalIpAddresses()) + { + try + { + CreateListener(localIp); + } + catch (Exception e) + { + _logger.ErrorException("Failed to Initilize Socket", e); + } + } } void _ssdpHandler_MessageReceived(object sender, SsdpMessageEventArgs e) @@ -89,8 +110,11 @@ namespace MediaBrowser.Dlna.Ssdp { if (e.LocalEndPoint == null) { - var ip = _appHost.LocalIpAddress; - e.LocalEndPoint = new IPEndPoint(IPAddress.Parse(ip), 0); + var ip = _appHost.LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i)); + if (ip != null) + { + e.LocalEndPoint = new IPEndPoint(ip, 0); + } } if (e.LocalEndPoint != null) @@ -107,29 +131,17 @@ namespace MediaBrowser.Dlna.Ssdp } } - private IEnumerable<NetworkInterface> GetNetworkInterfaces() - { - try - { - return NetworkInterface.GetAllNetworkInterfaces(); - } - catch (Exception ex) - { - _logger.ErrorException("Error in GetAllNetworkInterfaces", ex); - return new List<NetworkInterface>(); - } - } private void CreateListener(IPAddress localIp) { Task.Factory.StartNew(async (o) => { try { - var endPoint = new IPEndPoint(localIp, 1900); + _logger.Info("Creating SSDP listener on {0}", localIp); - var socket = GetMulticastSocket(localIp, endPoint); + var endPoint = new IPEndPoint(localIp, 1900); - _logger.Info("Creating SSDP listener on {0}", localIp); + var socket = GetMulticastSocket(localIp, endPoint); var receiveBuffer = new byte[64000]; diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs index 5dd05fd64..32c369a8f 100644 --- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs +++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs @@ -15,6 +15,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; using System.Threading.Tasks; +using Microsoft.Win32; namespace MediaBrowser.Dlna.Ssdp { @@ -112,7 +113,9 @@ namespace MediaBrowser.Dlna.Ssdp { get { - return _devices.Values.SelectMany(i => i).ToList(); + var devices = _devices.Values.ToList(); + + return devices.SelectMany(i => i).ToList(); } } @@ -121,6 +124,15 @@ namespace MediaBrowser.Dlna.Ssdp RestartSocketListener(); ReloadAliveNotifier(); + SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + } + + void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + if (e.Mode == PowerModes.Resume) + { + NotifyAll(); + } } public void SendSearchMessage(EndPoint localIp) @@ -433,6 +445,7 @@ namespace MediaBrowser.Dlna.Ssdp public void Dispose() { _config.NamedConfigurationUpdated -= _config_ConfigurationUpdated; + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; _isDisposed = true; while (_messageQueue.Count != 0) diff --git a/MediaBrowser.Dlna/packages.config b/MediaBrowser.Dlna/packages.config index fad6af08e..258a097cd 100644 --- a/MediaBrowser.Dlna/packages.config +++ b/MediaBrowser.Dlna/packages.config @@ -1,5 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> + <package id="morelinq" version="1.4.0" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> </packages>
\ No newline at end of file diff --git a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj index a74fe7e98..5ef8eaaa3 100644 --- a/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj +++ b/MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj @@ -79,14 +79,11 @@ <Compile Include="Providers\SeriesXmlProvider.cs" /> <Compile Include="Providers\VideoXmlProvider.cs" /> <Compile Include="Savers\BoxSetXmlSaver.cs" /> - <Compile Include="Savers\EpisodeXmlSaver.cs" /> <Compile Include="Savers\FolderXmlSaver.cs" /> <Compile Include="Savers\GameSystemXmlSaver.cs" /> <Compile Include="Savers\GameXmlSaver.cs" /> - <Compile Include="Savers\MovieXmlSaver.cs" /> <Compile Include="Savers\PersonXmlSaver.cs" /> <Compile Include="Savers\PlaylistXmlSaver.cs" /> - <Compile Include="Savers\SeriesXmlSaver.cs" /> <Compile Include="Savers\XmlSaverHelpers.cs" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs deleted file mode 100644 index dc8a16cd8..000000000 --- a/MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs +++ /dev/null @@ -1,166 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Security; -using System.Text; -using System.Threading; -using CommonIO; -using MediaBrowser.Common.IO; - -namespace MediaBrowser.LocalMetadata.Savers -{ - public class EpisodeXmlProvider : IMetadataFileSaver, IConfigurableProvider - { - private readonly IItemRepository _itemRepository; - - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; - private IFileSystem _fileSystem; - - public EpisodeXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) - { - _itemRepository = itemRepository; - _config = config; - _libraryManager = libraryManager; - _fileSystem = fileSystem; - } - - /// <summary> - /// Determines whether [is enabled for] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="updateType">Type of the update.</param> - /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns> - public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) - { - if (!item.SupportsLocalMetadata) - { - return false; - } - - return item is Episode && updateType >= ItemUpdateType.MetadataDownload; - } - - public string Name - { - get - { - return XmlProviderUtils.Name; - } - } - - public bool IsEnabled - { - get { return !_config.Configuration.DisableXmlSavers; } - } - - /// <summary> - /// Saves the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public void Save(IHasMetadata item, CancellationToken cancellationToken) - { - var episode = (Episode)item; - - var builder = new StringBuilder(); - - builder.Append("<Item>"); - - if (!string.IsNullOrEmpty(item.Name)) - { - builder.Append("<EpisodeName>" + SecurityElement.Escape(episode.Name) + "</EpisodeName>"); - } - - if (episode.IndexNumber.HasValue) - { - builder.Append("<EpisodeNumber>" + SecurityElement.Escape(episode.IndexNumber.Value.ToString(_usCulture)) + "</EpisodeNumber>"); - } - - if (episode.IndexNumberEnd.HasValue) - { - builder.Append("<EpisodeNumberEnd>" + SecurityElement.Escape(episode.IndexNumberEnd.Value.ToString(_usCulture)) + "</EpisodeNumberEnd>"); - } - - if (episode.AirsAfterSeasonNumber.HasValue) - { - builder.Append("<airsafter_season>" + SecurityElement.Escape(episode.AirsAfterSeasonNumber.Value.ToString(_usCulture)) + "</airsafter_season>"); - } - if (episode.AirsBeforeEpisodeNumber.HasValue) - { - builder.Append("<airsbefore_episode>" + SecurityElement.Escape(episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture)) + "</airsbefore_episode>"); - } - if (episode.AirsBeforeSeasonNumber.HasValue) - { - builder.Append("<airsbefore_season>" + SecurityElement.Escape(episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture)) + "</airsbefore_season>"); - } - - if (episode.ParentIndexNumber.HasValue) - { - builder.Append("<SeasonNumber>" + SecurityElement.Escape(episode.ParentIndexNumber.Value.ToString(_usCulture)) + "</SeasonNumber>"); - } - - if (episode.AbsoluteEpisodeNumber.HasValue) - { - builder.Append("<absolute_number>" + SecurityElement.Escape(episode.AbsoluteEpisodeNumber.Value.ToString(_usCulture)) + "</absolute_number>"); - } - - if (episode.DvdEpisodeNumber.HasValue) - { - builder.Append("<DVD_episodenumber>" + SecurityElement.Escape(episode.DvdEpisodeNumber.Value.ToString(_usCulture)) + "</DVD_episodenumber>"); - } - - if (episode.DvdSeasonNumber.HasValue) - { - builder.Append("<DVD_season>" + SecurityElement.Escape(episode.DvdSeasonNumber.Value.ToString(_usCulture)) + "</DVD_season>"); - } - - if (episode.PremiereDate.HasValue) - { - builder.Append("<FirstAired>" + SecurityElement.Escape(episode.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>"); - } - - XmlSaverHelpers.AddCommonNodes(episode, _libraryManager, builder); - XmlSaverHelpers.AddMediaInfo(episode, builder, _itemRepository); - - builder.Append("</Item>"); - - var xmlFilePath = GetSavePath(item); - - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> - { - "FirstAired", - "SeasonNumber", - "EpisodeNumber", - "EpisodeName", - "EpisodeNumberEnd", - "airsafter_season", - "airsbefore_episode", - "airsbefore_season", - "DVD_episodenumber", - "DVD_season", - "absolute_number" - - }, _config, _fileSystem); - } - - /// <summary> - /// Gets the save path. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - public string GetSavePath(IHasMetadata item) - { - var filename = Path.ChangeExtension(Path.GetFileName(item.Path), ".xml"); - - return Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename); - } - } -} diff --git a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs deleted file mode 100644 index 2e3e7aaa1..000000000 --- a/MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs +++ /dev/null @@ -1,147 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using System.Collections.Generic; -using System.IO; -using System.Security; -using System.Text; -using System.Threading; -using CommonIO; -using MediaBrowser.Common.IO; - -namespace MediaBrowser.LocalMetadata.Savers -{ - /// <summary> - /// Saves movie.xml for movies, trailers and music videos - /// </summary> - public class MovieXmlProvider : IMetadataFileSaver, IConfigurableProvider - { - private readonly IItemRepository _itemRepository; - private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; - private IFileSystem _fileSystem; - - public MovieXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) - { - _itemRepository = itemRepository; - _config = config; - _libraryManager = libraryManager; - _fileSystem = fileSystem; - } - - public string Name - { - get - { - return XmlProviderUtils.Name; - } - } - - public bool IsEnabled - { - get { return !_config.Configuration.DisableXmlSavers; } - } - - /// <summary> - /// Determines whether [is enabled for] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="updateType">Type of the update.</param> - /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns> - public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) - { - if (!item.SupportsLocalMetadata) - { - return false; - } - - var video = item as Video; - - // Check parent for null to avoid running this against things like video backdrops - if (video != null && !(item is Episode) && !video.IsOwnedItem) - { - return updateType >= ItemUpdateType.MetadataDownload; - } - - return false; - } - - /// <summary> - /// Saves the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public void Save(IHasMetadata item, CancellationToken cancellationToken) - { - var video = (Video)item; - - var builder = new StringBuilder(); - - builder.Append("<Title>"); - - XmlSaverHelpers.AddCommonNodes(video, _libraryManager, builder); - - var musicVideo = item as MusicVideo; - - if (musicVideo != null) - { - if (musicVideo.Artists.Count > 0) - { - builder.Append("<Artist>" + SecurityElement.Escape(string.Join(";", musicVideo.Artists.ToArray())) + "</Artist>"); - } - if (!string.IsNullOrEmpty(musicVideo.Album)) - { - builder.Append("<Album>" + SecurityElement.Escape(musicVideo.Album) + "</Album>"); - } - } - - var movie = item as Movie; - - if (movie != null) - { - if (!string.IsNullOrEmpty(movie.TmdbCollectionName)) - { - builder.Append("<TmdbCollectionName>" + SecurityElement.Escape(movie.TmdbCollectionName) + "</TmdbCollectionName>"); - } - } - - XmlSaverHelpers.AddMediaInfo(video, builder, _itemRepository); - - builder.Append("</Title>"); - - var xmlFilePath = GetSavePath(item); - - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> - { - // Deprecated. No longer saving in this field. - "IMDBrating", - - // Deprecated. No longer saving in this field. - "Description", - - "Artist", - "Album", - "TmdbCollectionName" - }, _config, _fileSystem); - } - - public string GetSavePath(IHasMetadata item) - { - return GetMovieSavePath((Video)item); - } - - public static string GetMovieSavePath(Video item) - { - if (item.IsInMixedFolder) - { - return Path.ChangeExtension(item.Path, ".xml"); - } - - return Path.Combine(item.ContainingFolderPath, "movie.xml"); - } - } -} diff --git a/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs b/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs deleted file mode 100644 index 9806c4216..000000000 --- a/MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs +++ /dev/null @@ -1,154 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Security; -using System.Text; -using System.Threading; -using CommonIO; -using MediaBrowser.Common.IO; - -namespace MediaBrowser.LocalMetadata.Savers -{ - public class SeriesXmlProvider : IMetadataFileSaver, IConfigurableProvider - { - private readonly IServerConfigurationManager _config; - private readonly ILibraryManager _libraryManager; - private IFileSystem _fileSystem; - - public SeriesXmlProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem) - { - _config = config; - _libraryManager = libraryManager; - _fileSystem = fileSystem; - } - - public string Name - { - get - { - return XmlProviderUtils.Name; - } - } - - /// <summary> - /// Determines whether [is enabled for] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="updateType">Type of the update.</param> - /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns> - public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType) - { - if (!item.SupportsLocalMetadata) - { - return false; - } - - return item is Series && updateType >= ItemUpdateType.MetadataDownload; - } - - public bool IsEnabled - { - get { return !_config.Configuration.DisableXmlSavers; } - } - - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Saves the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public void Save(IHasMetadata item, CancellationToken cancellationToken) - { - var series = (Series)item; - - var builder = new StringBuilder(); - - builder.Append("<Series>"); - - var tvdb = item.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(tvdb)) - { - builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>"); - } - - if (series.Status.HasValue) - { - builder.Append("<Status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</Status>"); - } - - if (series.Studios.Count > 0) - { - builder.Append("<Network>" + SecurityElement.Escape(series.Studios[0]) + "</Network>"); - } - - if (!string.IsNullOrEmpty(series.AirTime)) - { - builder.Append("<Airs_Time>" + SecurityElement.Escape(series.AirTime) + "</Airs_Time>"); - } - - if (series.AirDays != null) - { - if (series.AirDays.Count == 7) - { - builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>"); - } - else if (series.AirDays.Count > 0) - { - builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>"); - } - } - - if (series.PremiereDate.HasValue) - { - builder.Append("<FirstAired>" + SecurityElement.Escape(series.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>"); - } - - if (series.AnimeSeriesIndex.HasValue) - { - builder.Append("<AnimeSeriesIndex>" + SecurityElement.Escape(series.AnimeSeriesIndex.Value.ToString(UsCulture)) + "</AnimeSeriesIndex>"); - } - - XmlSaverHelpers.AddCommonNodes(series, _libraryManager, builder); - - builder.Append("</Series>"); - - var xmlFilePath = GetSavePath(item); - - XmlSaverHelpers.Save(builder, xmlFilePath, new List<string> - { - "id", - "Status", - "Network", - "Airs_Time", - "Airs_DayOfWeek", - "FirstAired", - - // Don't preserve old series node - "Series", - - "SeriesName", - - // Deprecated. No longer saving in this field. - "AnimeSeriesIndex" - }, _config, _fileSystem); - } - - /// <summary> - /// Gets the save path. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - public string GetSavePath(IHasMetadata item) - { - return Path.Combine(item.Path, "series.xml"); - } - } -} diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index afbd29578..9cdc4a7bf 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -505,7 +505,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { return "libx264"; } - if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(codec, "h265", StringComparison.OrdinalIgnoreCase) || string.Equals(codec, "hevc", StringComparison.OrdinalIgnoreCase)) { return "libx265"; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index f44d922d5..f436ca3a0 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -258,17 +258,17 @@ namespace MediaBrowser.MediaEncoding.Encoder var mediaInfo = new ProbeResultNormalizer(_logger, FileSystem).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol); - //var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); + var videoStream = mediaInfo.MediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - //if (videoStream != null) - //{ - // var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); + if (videoStream != null) + { + var isInterlaced = await DetectInterlaced(mediaInfo, videoStream, inputPath, probeSizeArgument).ConfigureAwait(false); - // if (isInterlaced) - // { - // videoStream.IsInterlaced = true; - // } - //} + if (isInterlaced) + { + videoStream.IsInterlaced = true; + } + } return mediaInfo; } @@ -292,16 +292,19 @@ namespace MediaBrowser.MediaEncoding.Encoder return false; } - var formats = (video.Container ?? string.Empty).Split(',').ToList(); - - // Take a shortcut and limit this to containers that are likely to have interlaced content - if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && - !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) + // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content + if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) == -1) { - return false; + var formats = (video.Container ?? string.Empty).Split(',').ToList(); + + if (!formats.Contains("vob", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("ts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) && + !formats.Contains("wtv", StringComparer.OrdinalIgnoreCase)) + { + return false; + } } var args = "{0} -i {1} -map 0:v:{2} -filter:v idet -frames:v 500 -an -f null /dev/null"; diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs index b52446ac6..791dc0cf1 100644 --- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs +++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs @@ -864,7 +864,7 @@ namespace MediaBrowser.MediaEncoding.Probing } } - private void ExtractTimestamp(Model.MediaInfo.MediaInfo video) + private void ExtractTimestamp(MediaInfo video) { if (video.VideoType == VideoType.VideoFile) { diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs index ba44ed7dd..882b2e1c2 100644 --- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs +++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs @@ -122,10 +122,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken) .ConfigureAwait(false); - using (var stream = subtitle.Item1) + var inputFormat = subtitle.Item2; + + if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null) { - var inputFormat = subtitle.Item2; + return subtitle.Item1; + } + using (var stream = subtitle.Item1) + { return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, cancellationToken).ConfigureAwait(false); } } @@ -288,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles return null; } - private ISubtitleWriter GetWriter(string format) + private ISubtitleWriter TryGetWriter(string format) { if (string.IsNullOrEmpty(format)) { @@ -312,6 +317,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles return new TtmlWriter(); } + return null; + } + + private ISubtitleWriter GetWriter(string format) + { + var writer = TryGetWriter(format); + + if (writer != null) + { + return writer; + } + throw new ArgumentException("Unsupported format: " + format); } diff --git a/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs b/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs index 764a7222f..dd6c77e66 100644 --- a/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs +++ b/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs @@ -11,16 +11,19 @@ namespace MediaBrowser.Model.Configuration public bool EnableIntrosParentalControl { get; set; } public bool EnableIntrosFromSimilarMovies { get; set; } public string CustomIntroPath { get; set; } + public string MediaInfoIntroPath { get; set; } public bool EnableIntrosFromUpcomingDvdMovies { get; set; } public bool EnableIntrosFromUpcomingStreamingMovies { get; set; } public int TrailerLimit { get; set; } + public string[] Tags { get; set; } public CinemaModeConfiguration() { EnableIntrosParentalControl = true; EnableIntrosFromSimilarMovies = true; TrailerLimit = 2; + Tags = new[] { "thx" }; } } } diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 7208ccb1d..08f7f89e3 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -63,6 +63,12 @@ namespace MediaBrowser.Model.Configuration public bool IsPortAuthorized { get; set; } /// <summary> + /// Gets or sets a value indicating whether [enable high quality image scaling]. + /// </summary> + /// <value><c>true</c> if [enable high quality image scaling]; otherwise, <c>false</c>.</value> + public bool EnableHighQualityImageScaling { get; set; } + + /// <summary> /// Gets or sets the item by name path. /// </summary> /// <value>The item by name path.</value> @@ -93,24 +99,6 @@ namespace MediaBrowser.Model.Configuration public bool EnableLocalizedGuids { get; set; } /// <summary> - /// Gets or sets a value indicating whether [disable startup scan]. - /// </summary> - /// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value> - public bool DisableStartupScan { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether [enable user views]. - /// </summary> - /// <value><c>true</c> if [enable user views]; otherwise, <c>false</c>.</value> - public bool EnableUserViews { get; set; } - - /// <summary> - /// Gets or sets a value indicating whether [enable library metadata sub folder]. - /// </summary> - /// <value><c>true</c> if [enable library metadata sub folder]; otherwise, <c>false</c>.</value> - public bool EnableLibraryMetadataSubFolder { get; set; } - - /// <summary> /// Gets or sets the preferred metadata language. /// </summary> /// <value>The preferred metadata language.</value> @@ -219,21 +207,20 @@ namespace MediaBrowser.Model.Configuration public int SharingExpirationDays { get; set; } - public bool DisableXmlSavers { get; set; } public bool EnableWindowsShortcuts { get; set; } - public bool EnableVideoFrameByFrameAnalysis { get; set; } - public bool EnableDateLastRefresh { get; set; } public string[] Migrations { get; set; } + public int MigrationVersion { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> public ServerConfiguration() { - Migrations = new string[] {}; + Migrations = new string[] { }; ImageSavingConvention = ImageSavingConvention.Compatible; PublicPort = 8096; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 278d22cfb..ab3a861a3 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -29,8 +29,6 @@ namespace MediaBrowser.Model.Configuration public bool GroupMoviesIntoBoxSets { get; set; } - public string[] DisplayChannelsWithinViews { get; set; } - public string[] ExcludeFoldersFromGrouping { get; set; } public string[] GroupedFolders { get; set; } @@ -50,7 +48,6 @@ namespace MediaBrowser.Model.Configuration public string[] PlainFolderViews { get; set; } public bool HidePlayedInLatest { get; set; } - public bool DisplayChannelsInline { get; set; } /// <summary> /// Initializes a new instance of the <see cref="UserConfiguration" /> class. @@ -62,8 +59,6 @@ namespace MediaBrowser.Model.Configuration LatestItemsExcludes = new string[] { }; OrderedViews = new string[] { }; - DisplayChannelsWithinViews = new string[] { }; - DisplayChannelsInline = true; PlainFolderViews = new string[] { }; diff --git a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs index 1f36c0976..a5a2e1e72 100644 --- a/MediaBrowser.Providers/Folders/DefaultImageProvider.cs +++ b/MediaBrowser.Providers/Folders/DefaultImageProvider.cs @@ -170,4 +170,4 @@ namespace MediaBrowser.Providers.Folders return GetSupportedImages(item).Any(i => !item.HasImage(i)); } } -} +}
\ No newline at end of file diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index f80b3f14d..083c8f1f3 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -97,7 +97,7 @@ namespace MediaBrowser.Providers.Manager var itemImageProvider = new ItemImageProvider(Logger, ProviderManager, ServerConfigurationManager, FileSystem); var localImagesFailed = false; - var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item).ToList(); + var allImageProviders = ((ProviderManager)ProviderManager).GetImageProviders(item, refreshOptions).ToList(); // Start by validating images try diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index f504c9612..477ff167c 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -249,17 +249,17 @@ namespace MediaBrowser.Providers.Manager }); } - public IEnumerable<IImageProvider> GetImageProviders(IHasImages item) + public IEnumerable<IImageProvider> GetImageProviders(IHasImages item, ImageRefreshOptions refreshOptions) { - return GetImageProviders(item, GetMetadataOptions(item), false); + return GetImageProviders(item, GetMetadataOptions(item), refreshOptions, false); } - private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, MetadataOptions options, bool includeDisabled) + private IEnumerable<IImageProvider> GetImageProviders(IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) { // Avoid implicitly captured closure var currentOptions = options; - return ImageProviders.Where(i => CanRefresh(i, item, options, includeDisabled)) + return ImageProviders.Where(i => CanRefresh(i, item, options, refreshOptions, includeDisabled)) .OrderBy(i => { // See if there's a user-defined order @@ -315,7 +315,7 @@ namespace MediaBrowser.Providers.Manager { var options = GetMetadataOptions(item); - return GetImageProviders(item, options, includeDisabled).OfType<IRemoteImageProvider>(); + return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_fileSystem)), includeDisabled).OfType<IRemoteImageProvider>(); } private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem) @@ -359,14 +359,17 @@ namespace MediaBrowser.Providers.Manager return true; } - private bool CanRefresh(IImageProvider provider, IHasImages item, MetadataOptions options, bool includeDisabled) + private bool CanRefresh(IImageProvider provider, IHasImages item, MetadataOptions options, ImageRefreshOptions refreshOptions, bool includeDisabled) { if (!includeDisabled) { // If locked only allow local providers if (item.IsLocked && !(provider is ILocalImageProvider)) { - return false; + if (refreshOptions.ImageRefreshMode != ImageRefreshMode.FullRefresh) + { + return false; + } } if (provider is IRemoteImageProvider || provider is IDynamicImageProvider) @@ -502,7 +505,7 @@ namespace MediaBrowser.Providers.Manager ItemType = typeof(T).Name }; - var imageProviders = GetImageProviders(dummy, options, true).ToList(); + var imageProviders = GetImageProviders(dummy, options, new ImageRefreshOptions(new DirectoryService(_fileSystem)), true).ToList(); AddMetadataPlugins(summary.Plugins, dummy, options); AddImagePlugins(summary.Plugins, dummy, imageProviders); @@ -1035,7 +1038,7 @@ namespace MediaBrowser.Providers.Manager .ToList(); var musicArtists = albums - .Select(i => i.Parent) + .Select(i => i.GetParent()) .OfType<MusicArtist>() .ToList(); diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 731d30d84..7478b2738 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -56,9 +56,8 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath> </Reference> - <Reference Include="MoreLinq, Version=1.1.18418.0, Culture=neutral, PublicKeyToken=384d532d7e88985d, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\morelinq.1.1.1\lib\net35\MoreLinq.dll</HintPath> + <Reference Include="MoreLinq"> + <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> diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index f0c5cbbd3..a575daacb 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -15,6 +15,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Text; using System.Threading; using System.Threading.Tasks; using CommonIO; @@ -130,6 +131,8 @@ namespace MediaBrowser.Providers.Subtitles try { + //var isText = MediaStream.IsTextFormat(response.Format); + using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) { await stream.CopyToAsync(fs).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs index 1a6327d00..50ecc6bbf 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeImageProvider.cs @@ -63,97 +63,89 @@ namespace MediaBrowser.Providers.TV var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); var indexOffset = TvdbSeriesProvider.GetSeriesOffset(series.ProviderIds) ?? 0; - var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber + indexOffset, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo()); - var result = files.Select(i => GetImageInfo(i, cancellationToken)) - .Where(i => i != null); + var result = nodes.Select(i => GetImageInfo(i, cancellationToken)) + .Where(i => i != null) + .ToList(); - return Task.FromResult(result); + return Task.FromResult<IEnumerable<RemoteImageInfo>>(result); } return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { }); } - private RemoteImageInfo GetImageInfo(FileSystemMetadata xmlFile, CancellationToken cancellationToken) + private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken) { var height = 225; var width = 400; var url = string.Empty; - using (var streamReader = new StreamReader(xmlFile.FullName, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "thumb_width": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - width = rval; - } - } - break; - } - - case "thumb_height": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - height = rval; - } - } - break; - } - - case "filename": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - url = TVUtils.BannerUrl + val; - } - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "thumb_width": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + width = rval; + } + } + break; + } + + case "thumb_height": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + height = rval; + } + } + break; + } + + case "filename": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + url = TVUtils.BannerUrl + val; + } + break; + } + + default: + reader.Skip(); + break; + } + } + } + } if (string.IsNullOrEmpty(url)) { @@ -205,11 +197,9 @@ namespace MediaBrowser.Providers.TV if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); - - var files = TvdbEpisodeProvider.Current.GetEpisodeXmlFiles(episode.ParentIndexNumber, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - return files.Any(i => _fileSystem.GetLastWriteTimeUtc(i) > date); + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; } } diff --git a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs index ae247c931..6b902ed8a 100644 --- a/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbEpisodeProvider.cs @@ -47,12 +47,22 @@ namespace MediaBrowser.Providers.TV { var list = new List<RemoteSearchResult>(); - if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && searchInfo.IndexNumber.HasValue) + // The search query must either provide an episode number or date + if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) + { + return list; + } + + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) { var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + + if (searchInfo.IndexNumber.HasValue) { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } + searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; @@ -97,47 +107,17 @@ namespace MediaBrowser.Providers.TV public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken) { - var identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); - - if (identity == null) - { - await Identify(searchInfo).ConfigureAwait(false); - identity = Identity.ParseIdentity(searchInfo.GetProviderId(FullIdKey)); - } - var result = new MetadataResult<Episode>(); - if (identity != null) - { - var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); - seriesProviderIds[MetadataProviders.Tvdb.ToString()] = identity.Value.SeriesId; - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - - var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = identity.Value.EpisodeNumber; - var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(searchInfo.SeriesProviderIds) ?? 0; - searchNumbers.SeasonNumber = identity.Value.SeasonIndex + seasonOffset; - searchNumbers.EpisodeNumberEnd = identity.Value.EpisodeNumberEnd ?? searchNumbers.EpisodeNumber; - - try - { - result = FetchEpisodeData(searchInfo, searchNumbers, seriesDataPath, cancellationToken); - } - catch (FileNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - catch (DirectoryNotFoundException) - { - // Don't fail the provider because this will just keep on going and going. - } - } - else if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && searchInfo.IndexNumber.HasValue) + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && + (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) { var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); var searchNumbers = new EpisodeNumbers(); - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + if (searchInfo.IndexNumber.HasValue) { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; @@ -176,11 +156,9 @@ namespace MediaBrowser.Providers.TV if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds)) { // Process images - var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds); + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(series.ProviderIds, series.GetPreferredMetadataLanguage()); - var files = GetEpisodeXmlFiles(episode.ParentIndexNumber, episode.IndexNumber, episode.IndexNumberEnd, seriesDataPath); - - return files.Any(i => _fileSystem.GetLastWriteTimeUtc(i) > date); + return _fileSystem.GetLastWriteTimeUtc(seriesXmlPath) > date; } return false; @@ -194,68 +172,22 @@ namespace MediaBrowser.Providers.TV /// <param name="endingEpisodeNumber">The ending episode number.</param> /// <param name="seriesDataPath">The series data path.</param> /// <returns>List{FileInfo}.</returns> - internal List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) { - var files = new List<FileSystemMetadata>(); - - if (episodeNumber == null) - { - return files; - } - - if (seasonNumber == null) - { - return files; - } - - var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - - var fileInfo = _fileSystem.GetFileInfo(file); - var usingAbsoluteData = false; - - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - usingAbsoluteData = true; - } - } - - var end = endingEpisodeNumber ?? episodeNumber; - episodeNumber++; - - while (episodeNumber <= end) - { - if (usingAbsoluteData) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - } - - fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - break; - } - - episodeNumber++; - } - - return files; + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath (searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); + + try + { + return GetXmlNodes(seriesXmlPath, searchInfo); + } + catch (DirectoryNotFoundException) + { + return new List<XmlReader> (); + } + catch (FileNotFoundException) + { + return new List<XmlReader> (); + } } private class EpisodeNumbers @@ -275,368 +207,544 @@ namespace MediaBrowser.Providers.TV /// <returns>Task{System.Boolean}.</returns> private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken) { - var episodeNumber = searchNumbers.EpisodeNumber; - var seasonNumber = searchNumbers.SeasonNumber; - - string file; - var usingAbsoluteData = false; - - var result = new MetadataResult<Episode>() - { - Item = new Episode - { - IndexNumber = id.IndexNumber, - ParentIndexNumber = id.ParentIndexNumber, - IndexNumberEnd = id.IndexNumberEnd - } - }; - - try - { - if (seasonNumber != null) - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - FetchMainEpisodeInfo(result, file, cancellationToken); + var result = new MetadataResult<Episode>() + { + Item = new Episode + { + IndexNumber = id.IndexNumber, + ParentIndexNumber = id.ParentIndexNumber, + IndexNumberEnd = id.IndexNumberEnd + } + }; - result.HasMetadata = true; - } - } - catch (FileNotFoundException) - { - // Could be using absolute numbering - if (seasonNumber.HasValue && seasonNumber.Value != 1) - { - throw; - } - } - - if (!result.HasMetadata) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - - FetchMainEpisodeInfo(result, file, cancellationToken); - result.HasMetadata = true; - usingAbsoluteData = true; - } + var xmlNodes = GetEpisodeXmlNodes (seriesDataPath, id); - var end = searchNumbers.EpisodeNumberEnd; - episodeNumber++; + if (xmlNodes.Count > 0) { + FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken); - while (episodeNumber <= end) - { - if (usingAbsoluteData) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - } - - try - { - FetchAdditionalPartInfo(result, file, cancellationToken); - } - catch (FileNotFoundException) - { - break; - } - catch (DirectoryNotFoundException) - { - break; - } + result.HasMetadata = true; + } - episodeNumber++; - } + foreach (var node in xmlNodes.Skip(1)) { + FetchAdditionalPartInfo(result, node, cancellationToken); + } return result; } + private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) + { + var list = new List<XmlReader> (); + + if (searchInfo.IndexNumber.HasValue) + { + var files = GetEpisodeXmlFiles (searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName (xmlFile)); + + list = files.Select (GetXmlReader).ToList (); + } + + if (list.Count == 0 && searchInfo.PremiereDate.HasValue) { + list = GetXmlNodesByPremiereDate (xmlFile, searchInfo.PremiereDate.Value); + } + + return list; + } + + private List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + { + var files = new List<FileSystemMetadata>(); + + if (episodeNumber == null) + { + return files; + } + + if (seasonNumber == null) + { + return files; + } + + var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); + + var fileInfo = _fileSystem.GetFileInfo(file); + var usingAbsoluteData = false; + + if (fileInfo.Exists) + { + files.Add(fileInfo); + } + else + { + file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + fileInfo = _fileSystem.GetFileInfo(file); + if (fileInfo.Exists) + { + files.Add(fileInfo); + usingAbsoluteData = true; + } + } + + var end = endingEpisodeNumber ?? episodeNumber; + episodeNumber++; + + while (episodeNumber <= end) + { + if (usingAbsoluteData) + { + file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + } + else + { + file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); + } + + fileInfo = _fileSystem.GetFileInfo(file); + if (fileInfo.Exists) + { + files.Add(fileInfo); + } + else + { + break; + } + + episodeNumber++; + } + + return files; + } + + private XmlReader GetXmlReader(FileSystemMetadata xmlFile) + { + return GetXmlReader (_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8)); + } + + private XmlReader GetXmlReader(String xml) + { + var streamReader = new StringReader (xml); + + return XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + }); + } + + private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) + { + var list = new List<XmlReader> (); + + using (var streamReader = new StreamReader (xmlFile, Encoding.UTF8)) { + // Use XmlReader for best performance + using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Episode": + { + var outerXml = reader.ReadOuterXml(); + + var airDate = GetEpisodeAirDate (outerXml); + + if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) + { + list.Add (GetXmlReader(outerXml)); + return list; + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } + + return list; + } + + private DateTime? GetEpisodeAirDate(string xml) + { + using (var streamReader = new StringReader (xml)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent (); + + // Loop through each element + while (reader.Read ()) { + + if (reader.NodeType == XmlNodeType.Element) { + switch (reader.Name) { + + case "FirstAired": + { + var val = reader.ReadElementContentAsString (); + + if (!string.IsNullOrWhiteSpace (val)) { + DateTime date; + if (DateTime.TryParse (val, out date)) { + date = date.ToUniversalTime (); + + return date; + } + } + + break; + } + + default: + reader.Skip (); + break; + } + } + } + } + } + return null; + } + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private void FetchMainEpisodeInfo(MetadataResult<Episode> result, string xmlFile, CancellationToken cancellationToken) + private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - result.ResetPeople(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + result.ResetPeople(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "id": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Tvdb, val); + } + break; + } + + case "IMDB_ID": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Imdb, val); + } + break; + } + + case "DVD_episodenumber": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float num; + + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + { + item.DvdEpisodeNumber = num; + } + } + + break; + } + + case "DVD_season": { - case "id": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Tvdb, val); - } - break; - } - - case "IMDB_ID": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.SetProviderId(MetadataProviders.Imdb, val); - } - break; - } - - case "DVD_episodenumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) - { - item.DvdEpisodeNumber = num; - } - } - - break; - } - - case "DVD_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) - { - item.DvdSeasonNumber = Convert.ToInt32(num); - } - } - - break; - } - - case "absolute_number": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AbsoluteEpisodeNumber = rval; - } - } - - break; - } - - case "airsbefore_episode": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeEpisodeNumber = rval; - } - } - - break; - } - - case "airsafter_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsAfterSeasonNumber = rval; - } - } + var val = reader.ReadElementContentAsString(); - break; - } + if (!string.IsNullOrWhiteSpace(val)) + { + float num; - case "airsbefore_season": + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeSeasonNumber = rval; - } - } - - break; + item.DvdSeasonNumber = Convert.ToInt32(num); } + } - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name = val; - } - } - break; - } - - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview = val; - } - } - break; - } - case "Rating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float rval; - - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) - { - item.CommunityRating = rval; - } - } - break; - } - case "RatingCount": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.VoteCount = rval; - } - } + break; + } - break; - } + case "EpisodeNumber": + { + if (!item.IndexNumber.HasValue) + { + var val = reader.ReadElementContentAsString(); - case "FirstAired": + if (!string.IsNullOrWhiteSpace(val)) { - var val = reader.ReadElementContentAsString(); + int rval; - if (!string.IsNullOrWhiteSpace(val)) + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - DateTime date; - if (DateTime.TryParse(val, out date)) - { - date = date.ToUniversalTime(); - - item.PremiereDate = date; - item.ProductionYear = date.Year; - } + item.IndexNumber = rval; } - - break; } + } - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); + break; + } - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } + case "SeasonNumber": + { + if (!item.ParentIndexNumber.HasValue) + { + var val = reader.ReadElementContentAsString(); - break; - } - case "Writer": + if (!string.IsNullOrWhiteSpace(val)) { - var val = reader.ReadElementContentAsString(); + int rval; - if (!string.IsNullOrWhiteSpace(val)) + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Writer); - } + item.ParentIndexNumber = rval; } - - break; } + } - default: - reader.Skip(); - break; + break; } - } - } - } - } + + case "absolute_number": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AbsoluteEpisodeNumber = rval; + } + } + + break; + } + + case "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeEpisodeNumber = rval; + } + } + + break; + } + + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + + break; + } + + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name = val; + } + } + break; + } + + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview = val; + } + } + break; + } + case "Rating": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float rval; + + // float.TryParse is local aware, so it can be probamatic, force us culture + if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) + { + item.CommunityRating = rval; + } + } + break; + } + case "RatingCount": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.VoteCount = rval; + } + } + + break; + } + + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out date)) + { + date = date.ToUniversalTime(); + + item.PremiereDate = date; + item.ProductionYear = date.Year; + } + } + + break; + } + + case "Director": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Director); + } + } + + break; + } + case "GuestStars": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddGuestStars(result, val); + } + } + + break; + } + case "Writer": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Writer); + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } } private void AddPeople<T>(MetadataResult<T> result, string val, string personType) @@ -680,108 +788,99 @@ namespace MediaBrowser.Providers.TV } } - private void FetchAdditionalPartInfo(MetadataResult<Episode> result, string xmlFile, CancellationToken cancellationToken) + private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name += ", " + val; - } - } - break; - } - - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview += Environment.NewLine + Environment.NewLine + val; - } - } - break; - } - case "Director": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Director); - } - } - - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } - - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Writer); - } - } - - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name += ", " + val; + } + } + break; + } + + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview += Environment.NewLine + Environment.NewLine + val; + } + } + break; + } + case "Director": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Director); + } + } + + break; + } + case "GuestStars": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddGuestStars(result, val); + } + } + + break; + } + case "Writer": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Writer); + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } } public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index d63022900..7250dbee4 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -161,9 +161,7 @@ namespace MediaBrowser.Providers.TV var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - var seriesXmlFilename = metadataLanguage.ToLower() + ".xml"; - - var seriesXmlPath = Path.Combine(seriesDataPath, seriesXmlFilename); + var seriesXmlPath = GetSeriesXmlPath (seriesProviderIds, metadataLanguage); var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); FetchSeriesInfo(series, seriesXmlPath, cancellationToken); @@ -1278,6 +1276,15 @@ namespace MediaBrowser.Providers.TV return null; } + public string GetSeriesXmlPath(Dictionary<string, string> seriesProviderIds, string language) + { + var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); + + var seriesXmlFilename = language.ToLower() + ".xml"; + + return Path.Combine (seriesDataPath, seriesXmlFilename); + } + /// <summary> /// Gets the series data path. /// </summary> diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config index 9ff08e4f4..9002f1a40 100644 --- a/MediaBrowser.Providers/packages.config +++ b/MediaBrowser.Providers/packages.config @@ -2,7 +2,7 @@ <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> <package id="MediaBrowser.BdInfo" version="1.0.0.10" 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="taglib" version="2.1.0.0" targetFramework="net45" /> </packages>
\ No newline at end of file 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/Configuration/ServerConfigurationManager.cs b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs index a7d3854e7..2f4c3f5ee 100644 --- a/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs +++ b/MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs @@ -171,12 +171,36 @@ namespace MediaBrowser.Server.Implementations.Configuration ValidateItemByNamePath(newConfig); ValidatePathSubstitutions(newConfig); ValidateMetadataPath(newConfig); + ValidateSslCertificate(newConfig); EventHelper.FireEventIfNotNull(ConfigurationUpdating, this, new GenericEventArgs<ServerConfiguration> { Argument = newConfig }, Logger); base.ReplaceConfiguration(newConfiguration); } + + /// <summary> + /// Validates the SSL certificate. + /// </summary> + /// <param name="newConfig">The new configuration.</param> + /// <exception cref="System.IO.DirectoryNotFoundException"></exception> + private void ValidateSslCertificate(BaseApplicationConfiguration newConfig) + { + var serverConfig = (ServerConfiguration)newConfig; + + var newPath = serverConfig.CertificatePath; + + if (!string.IsNullOrWhiteSpace(newPath) + && !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath)) + { + // Validate + if (!FileSystem.FileExists(newPath)) + { + throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath)); + } + } + } + private void ValidatePathSubstitutions(ServerConfiguration newConfig) { foreach (var map in newConfig.PathSubstitutions) 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/UsageEntryPoint.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs index f9a141da3..c3b9c0d4d 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageEntryPoint.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints { try { - await new UsageReporter(_applicationHost, _httpClient, _userManager) + await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger) .ReportAppUsage(client, CancellationToken.None) .ConfigureAwait(false); } @@ -108,7 +108,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints { try { - await new UsageReporter(_applicationHost, _httpClient, _userManager) + await new UsageReporter(_applicationHost, _httpClient, _userManager, _logger) .ReportServerUsage(CancellationToken.None) .ConfigureAwait(false); } diff --git a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs index 77dd54a80..5496bd9b2 100644 --- a/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs +++ b/MediaBrowser.Server.Implementations/EntryPoints/UsageReporter.cs @@ -8,6 +8,7 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Logging; namespace MediaBrowser.Server.Implementations.EntryPoints { @@ -16,13 +17,15 @@ namespace MediaBrowser.Server.Implementations.EntryPoints private readonly IApplicationHost _applicationHost; private readonly IHttpClient _httpClient; private readonly IUserManager _userManager; + private readonly ILogger _logger; private const string MbAdminUrl = "http://www.mb3admin.com/admin/"; - public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager) + public UsageReporter(IApplicationHost applicationHost, IHttpClient httpClient, IUserManager userManager, ILogger logger) { _applicationHost = applicationHost; _httpClient = httpClient; _userManager = userManager; + _logger = logger; } public Task ReportServerUsage(CancellationToken cancellationToken) @@ -47,7 +50,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints data["linkedusers"] = users.Count(i => i.ConnectLinkType.HasValue && i.ConnectLinkType.Value == UserLinkType.LinkedUser).ToString(CultureInfo.InvariantCulture); data["plugins"] = string.Join(",", _applicationHost.Plugins.Select(i => i.Id).ToArray()); - + return _httpClient.Post(MbAdminUrl + "service/registration/ping", data, cancellationToken); } @@ -58,6 +61,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints throw new ArgumentException("Client info must have a device Id"); } + _logger.Info("App Activity: app: {0}, version: {1}, deviceId: {2}, deviceName: {3}", + app.AppName ?? "Unknown App", + app.AppVersion ?? "Unknown", + app.DeviceId, + app.DeviceName ?? "Unknown"); + cancellationToken.ThrowIfCancellationRequested(); var data = new Dictionary<string, string> 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/HttpServer/HttpListenerHost.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs index 6ade9a8f6..0283c1f7a 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -19,6 +19,7 @@ using System.Linq; using System.Reflection; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.Net; using MediaBrowser.Common.Security; namespace MediaBrowser.Server.Implementations.HttpServer @@ -39,40 +40,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer public event EventHandler<WebSocketConnectEventArgs> WebSocketConnected; public event EventHandler<WebSocketConnectingEventArgs> WebSocketConnecting; - private readonly List<string> _localEndpoints = new List<string>(); - - private readonly ReaderWriterLockSlim _localEndpointLock = new ReaderWriterLockSlim(); - public string CertificatePath { get; private set; } private readonly IServerConfigurationManager _config; - - /// <summary> - /// Gets the local end points. - /// </summary> - /// <value>The local end points.</value> - public IEnumerable<string> LocalEndPoints - { - get - { - _localEndpointLock.EnterReadLock(); - - var list = _localEndpoints.ToList(); - - _localEndpointLock.ExitReadLock(); - - return list; - } - } + private readonly INetworkManager _networkManager; public HttpListenerHost(IApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, params Assembly[] assembliesWithServices) + string defaultRedirectPath, INetworkManager networkManager, params Assembly[] assembliesWithServices) : base(serviceName, assembliesWithServices) { DefaultRedirectPath = defaultRedirectPath; + _networkManager = networkManager; _config = config; _logger = logManager.GetLogger("HttpServer"); @@ -175,26 +156,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer private void OnRequestReceived(string localEndPoint) { - var ignore = localEndPoint.IndexOf("::", StringComparison.OrdinalIgnoreCase) != -1 || - - localEndPoint.StartsWith("127.", StringComparison.OrdinalIgnoreCase) || - localEndPoint.StartsWith("localhost", StringComparison.OrdinalIgnoreCase) || - localEndPoint.StartsWith("169.", StringComparison.OrdinalIgnoreCase); - - if (ignore) - { - return; - } - - if (_localEndpointLock.TryEnterWriteLock(100)) - { - var list = _localEndpoints.ToList(); - - list.Remove(localEndPoint); - list.Insert(0, localEndPoint); - - _localEndpointLock.ExitWriteLock(); - } } /// <summary> diff --git a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs index 4d81ec157..cc351f6b3 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/ServerFactory.cs @@ -1,4 +1,5 @@ using MediaBrowser.Common; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Net; using MediaBrowser.Model.Logging; @@ -17,18 +18,20 @@ namespace MediaBrowser.Server.Implementations.HttpServer /// <param name="applicationHost">The application host.</param> /// <param name="logManager">The log manager.</param> /// <param name="config">The configuration.</param> + /// <param name="_networkmanager">The _networkmanager.</param> /// <param name="serverName">Name of the server.</param> /// <param name="defaultRedirectpath">The default redirectpath.</param> /// <returns>IHttpServer.</returns> public static IHttpServer CreateServer(IApplicationHost applicationHost, ILogManager logManager, IServerConfigurationManager config, + INetworkManager _networkmanager, string serverName, string defaultRedirectpath) { LogManager.LogFactory = new ServerLogFactory(logManager); - return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath); + return new HttpListenerHost(applicationHost, logManager, config, serverName, defaultRedirectpath, _networkmanager); } } } diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs new file mode 100644 index 000000000..3ef48d13a --- /dev/null +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/HttpUtility.cs @@ -0,0 +1,941 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Text; + +namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp +{ + public static class MyHttpUtility + { + sealed class HttpQSCollection : NameValueCollection + { + public override string ToString() + { + int count = Count; + if (count == 0) + return ""; + StringBuilder sb = new StringBuilder(); + string[] keys = AllKeys; + for (int i = 0; i < count; i++) + { + sb.AppendFormat("{0}={1}&", keys[i], this[keys[i]]); + } + if (sb.Length > 0) + sb.Length--; + return sb.ToString(); + } + } + + // Must be sorted + static readonly long[] entities = new long[] { + (long)'A' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'A' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'A' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, + (long)'A' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, + (long)'A' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'A' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'B' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'C' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'C' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'D' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, + (long)'D' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'E' << 56 | (long)'T' << 48 | (long)'H' << 40, + (long)'E' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'E' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'E' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'E' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'E' << 56 | (long)'t' << 48 | (long)'a' << 40, + (long)'E' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'G' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'I' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'I' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'I' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'I' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'I' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'K' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, + (long)'L' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, + (long)'M' << 56 | (long)'u' << 48, + (long)'N' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'N' << 56 | (long)'u' << 48, + (long)'O' << 56 | (long)'E' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'O' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'O' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, + (long)'O' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'O' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, + (long)'O' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'O' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'P' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'P' << 56 | (long)'i' << 48, + (long)'P' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, + (long)'P' << 56 | (long)'s' << 48 | (long)'i' << 40, + (long)'R' << 56 | (long)'h' << 48 | (long)'o' << 40, + (long)'S' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, + (long)'S' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'T' << 56 | (long)'H' << 48 | (long)'O' << 40 | (long)'R' << 32 | (long)'N' << 24, + (long)'T' << 56 | (long)'a' << 48 | (long)'u' << 40, + (long)'T' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'U' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'U' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'U' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'U' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'U' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'X' << 56 | (long)'i' << 48, + (long)'Y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'Y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'Z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'a' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'a' << 56 | (long)'c' << 48 | (long)'u' << 40 | (long)'t' << 32 | (long)'e' << 24, + (long)'a' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'a' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'l' << 48 | (long)'e' << 40 | (long)'f' << 32 | (long)'s' << 24 | (long)'y' << 16 | (long)'m' << 8, + (long)'a' << 56 | (long)'l' << 48 | (long)'p' << 40 | (long)'h' << 32 | (long)'a' << 24, + (long)'a' << 56 | (long)'m' << 48 | (long)'p' << 40, + (long)'a' << 56 | (long)'n' << 48 | (long)'d' << 40, + (long)'a' << 56 | (long)'n' << 48 | (long)'g' << 40, + (long)'a' << 56 | (long)'p' << 48 | (long)'o' << 40 | (long)'s' << 32, + (long)'a' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'g' << 24, + (long)'a' << 56 | (long)'s' << 48 | (long)'y' << 40 | (long)'m' << 32 | (long)'p' << 24, + (long)'a' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'a' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'b' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'b' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'b' << 56 | (long)'r' << 48 | (long)'v' << 40 | (long)'b' << 32 | (long)'a' << 24 | (long)'r' << 16, + (long)'b' << 56 | (long)'u' << 48 | (long)'l' << 40 | (long)'l' << 32, + (long)'c' << 56 | (long)'a' << 48 | (long)'p' << 40, + (long)'c' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'d' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'c' << 56 | (long)'e' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'c' << 56 | (long)'e' << 48 | (long)'n' << 40 | (long)'t' << 32, + (long)'c' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'c' << 56 | (long)'i' << 48 | (long)'r' << 40 | (long)'c' << 32, + (long)'c' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'b' << 32 | (long)'s' << 24, + (long)'c' << 56 | (long)'o' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'c' << 56 | (long)'o' << 48 | (long)'p' << 40 | (long)'y' << 32, + (long)'c' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'r' << 24, + (long)'c' << 56 | (long)'u' << 48 | (long)'p' << 40, + (long)'c' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'n' << 16, + (long)'d' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'d' << 56 | (long)'a' << 48 | (long)'g' << 40 | (long)'g' << 32 | (long)'e' << 24 | (long)'r' << 16, + (long)'d' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'d' << 56 | (long)'e' << 48 | (long)'g' << 40, + (long)'d' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'d' << 56 | (long)'i' << 48 | (long)'a' << 40 | (long)'m' << 32 | (long)'s' << 24, + (long)'d' << 56 | (long)'i' << 48 | (long)'v' << 40 | (long)'i' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'e' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'e' << 56 | (long)'m' << 48 | (long)'p' << 40 | (long)'t' << 32 | (long)'y' << 24, + (long)'e' << 56 | (long)'m' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'e' << 56 | (long)'n' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'e' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'e' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'i' << 32 | (long)'v' << 24, + (long)'e' << 56 | (long)'t' << 48 | (long)'a' << 40, + (long)'e' << 56 | (long)'t' << 48 | (long)'h' << 40, + (long)'e' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'e' << 56 | (long)'u' << 48 | (long)'r' << 40 | (long)'o' << 32, + (long)'e' << 56 | (long)'x' << 48 | (long)'i' << 40 | (long)'s' << 32 | (long)'t' << 24, + (long)'f' << 56 | (long)'n' << 48 | (long)'o' << 40 | (long)'f' << 32, + (long)'f' << 56 | (long)'o' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'l' << 24 | (long)'l' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'2' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'1' << 24 | (long)'4' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'c' << 32 | (long)'3' << 24 | (long)'4' << 16, + (long)'f' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'l' << 24, + (long)'g' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'g' << 56 | (long)'e' << 48, + (long)'g' << 56 | (long)'t' << 48, + (long)'h' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'h' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'h' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'t' << 24 | (long)'s' << 16, + (long)'h' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'l' << 32 | (long)'i' << 24 | (long)'p' << 16, + (long)'i' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'i' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'i' << 56 | (long)'e' << 48 | (long)'x' << 40 | (long)'c' << 32 | (long)'l' << 24, + (long)'i' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'i' << 56 | (long)'m' << 48 | (long)'a' << 40 | (long)'g' << 32 | (long)'e' << 24, + (long)'i' << 56 | (long)'n' << 48 | (long)'f' << 40 | (long)'i' << 32 | (long)'n' << 24, + (long)'i' << 56 | (long)'n' << 48 | (long)'t' << 40, + (long)'i' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'i' << 56 | (long)'q' << 48 | (long)'u' << 40 | (long)'e' << 32 | (long)'s' << 24 | (long)'t' << 16, + (long)'i' << 56 | (long)'s' << 48 | (long)'i' << 40 | (long)'n' << 32, + (long)'i' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'k' << 56 | (long)'a' << 48 | (long)'p' << 40 | (long)'p' << 32 | (long)'a' << 24, + (long)'l' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'l' << 56 | (long)'a' << 48 | (long)'m' << 40 | (long)'b' << 32 | (long)'d' << 24 | (long)'a' << 16, + (long)'l' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'l' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'l' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'l' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'e' << 48, + (long)'l' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, + (long)'l' << 56 | (long)'o' << 48 | (long)'w' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'t' << 16, + (long)'l' << 56 | (long)'o' << 48 | (long)'z' << 40, + (long)'l' << 56 | (long)'r' << 48 | (long)'m' << 40, + (long)'l' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, + (long)'l' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'l' << 56 | (long)'t' << 48, + (long)'m' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'r' << 32, + (long)'m' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, + (long)'m' << 56 | (long)'i' << 48 | (long)'c' << 40 | (long)'r' << 32 | (long)'o' << 24, + (long)'m' << 56 | (long)'i' << 48 | (long)'d' << 40 | (long)'d' << 32 | (long)'o' << 24 | (long)'t' << 16, + (long)'m' << 56 | (long)'i' << 48 | (long)'n' << 40 | (long)'u' << 32 | (long)'s' << 24, + (long)'m' << 56 | (long)'u' << 48, + (long)'n' << 56 | (long)'a' << 48 | (long)'b' << 40 | (long)'l' << 32 | (long)'a' << 24, + (long)'n' << 56 | (long)'b' << 48 | (long)'s' << 40 | (long)'p' << 32, + (long)'n' << 56 | (long)'d' << 48 | (long)'a' << 40 | (long)'s' << 32 | (long)'h' << 24, + (long)'n' << 56 | (long)'e' << 48, + (long)'n' << 56 | (long)'i' << 48, + (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40, + (long)'n' << 56 | (long)'o' << 48 | (long)'t' << 40 | (long)'i' << 32 | (long)'n' << 24, + (long)'n' << 56 | (long)'s' << 48 | (long)'u' << 40 | (long)'b' << 32, + (long)'n' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'n' << 56 | (long)'u' << 48, + (long)'o' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'o' << 56 | (long)'e' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'o' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'l' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'e' << 24, + (long)'o' << 56 | (long)'m' << 48 | (long)'e' << 40 | (long)'g' << 32 | (long)'a' << 24, + (long)'o' << 56 | (long)'m' << 48 | (long)'i' << 40 | (long)'c' << 32 | (long)'r' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'o' << 56 | (long)'p' << 48 | (long)'l' << 40 | (long)'u' << 32 | (long)'s' << 24, + (long)'o' << 56 | (long)'r' << 48, + (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'f' << 32, + (long)'o' << 56 | (long)'r' << 48 | (long)'d' << 40 | (long)'m' << 32, + (long)'o' << 56 | (long)'s' << 48 | (long)'l' << 40 | (long)'a' << 32 | (long)'s' << 24 | (long)'h' << 16, + (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'l' << 32 | (long)'d' << 24 | (long)'e' << 16, + (long)'o' << 56 | (long)'t' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24 | (long)'s' << 16, + (long)'o' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'a' << 32, + (long)'p' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'t' << 32, + (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'m' << 32 | (long)'i' << 24 | (long)'l' << 16, + (long)'p' << 56 | (long)'e' << 48 | (long)'r' << 40 | (long)'p' << 32, + (long)'p' << 56 | (long)'h' << 48 | (long)'i' << 40, + (long)'p' << 56 | (long)'i' << 48, + (long)'p' << 56 | (long)'i' << 48 | (long)'v' << 40, + (long)'p' << 56 | (long)'l' << 48 | (long)'u' << 40 | (long)'s' << 32 | (long)'m' << 24 | (long)'n' << 16, + (long)'p' << 56 | (long)'o' << 48 | (long)'u' << 40 | (long)'n' << 32 | (long)'d' << 24, + (long)'p' << 56 | (long)'r' << 48 | (long)'i' << 40 | (long)'m' << 32 | (long)'e' << 24, + (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'d' << 32, + (long)'p' << 56 | (long)'r' << 48 | (long)'o' << 40 | (long)'p' << 32, + (long)'p' << 56 | (long)'s' << 48 | (long)'i' << 40, + (long)'q' << 56 | (long)'u' << 48 | (long)'o' << 40 | (long)'t' << 32, + (long)'r' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'r' << 56 | (long)'a' << 48 | (long)'d' << 40 | (long)'i' << 32 | (long)'c' << 24, + (long)'r' << 56 | (long)'a' << 48 | (long)'n' << 40 | (long)'g' << 32, + (long)'r' << 56 | (long)'a' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'r' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'r' << 56 | (long)'c' << 48 | (long)'e' << 40 | (long)'i' << 32 | (long)'l' << 24, + (long)'r' << 56 | (long)'d' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'r' << 56 | (long)'e' << 48 | (long)'a' << 40 | (long)'l' << 32, + (long)'r' << 56 | (long)'e' << 48 | (long)'g' << 40, + (long)'r' << 56 | (long)'f' << 48 | (long)'l' << 40 | (long)'o' << 32 | (long)'o' << 24 | (long)'r' << 16, + (long)'r' << 56 | (long)'h' << 48 | (long)'o' << 40, + (long)'r' << 56 | (long)'l' << 48 | (long)'m' << 40, + (long)'r' << 56 | (long)'s' << 48 | (long)'a' << 40 | (long)'q' << 32 | (long)'u' << 24 | (long)'o' << 16, + (long)'r' << 56 | (long)'s' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'s' << 56 | (long)'b' << 48 | (long)'q' << 40 | (long)'u' << 32 | (long)'o' << 24, + (long)'s' << 56 | (long)'c' << 48 | (long)'a' << 40 | (long)'r' << 32 | (long)'o' << 24 | (long)'n' << 16, + (long)'s' << 56 | (long)'d' << 48 | (long)'o' << 40 | (long)'t' << 32, + (long)'s' << 56 | (long)'e' << 48 | (long)'c' << 40 | (long)'t' << 32, + (long)'s' << 56 | (long)'h' << 48 | (long)'y' << 40, + (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24, + (long)'s' << 56 | (long)'i' << 48 | (long)'g' << 40 | (long)'m' << 32 | (long)'a' << 24 | (long)'f' << 16, + (long)'s' << 56 | (long)'i' << 48 | (long)'m' << 40, + (long)'s' << 56 | (long)'p' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24 | (long)'s' << 16, + (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'b' << 40 | (long)'e' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'m' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'1' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'2' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'3' << 32, + (long)'s' << 56 | (long)'u' << 48 | (long)'p' << 40 | (long)'e' << 32, + (long)'s' << 56 | (long)'z' << 48 | (long)'l' << 40 | (long)'i' << 32 | (long)'g' << 24, + (long)'t' << 56 | (long)'a' << 48 | (long)'u' << 40, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'r' << 32 | (long)'e' << 24 | (long)'4' << 16, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24, + (long)'t' << 56 | (long)'h' << 48 | (long)'e' << 40 | (long)'t' << 32 | (long)'a' << 24 | (long)'s' << 16 | (long)'y' << 8 | (long)'m' << 0, + (long)'t' << 56 | (long)'h' << 48 | (long)'i' << 40 | (long)'n' << 32 | (long)'s' << 24 | (long)'p' << 16, + (long)'t' << 56 | (long)'h' << 48 | (long)'o' << 40 | (long)'r' << 32 | (long)'n' << 24, + (long)'t' << 56 | (long)'i' << 48 | (long)'l' << 40 | (long)'d' << 32 | (long)'e' << 24, + (long)'t' << 56 | (long)'i' << 48 | (long)'m' << 40 | (long)'e' << 32 | (long)'s' << 24, + (long)'t' << 56 | (long)'r' << 48 | (long)'a' << 40 | (long)'d' << 32 | (long)'e' << 24, + (long)'u' << 56 | (long)'A' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'u' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'u' << 56 | (long)'a' << 48 | (long)'r' << 40 | (long)'r' << 32, + (long)'u' << 56 | (long)'c' << 48 | (long)'i' << 40 | (long)'r' << 32 | (long)'c' << 24, + (long)'u' << 56 | (long)'g' << 48 | (long)'r' << 40 | (long)'a' << 32 | (long)'v' << 24 | (long)'e' << 16, + (long)'u' << 56 | (long)'m' << 48 | (long)'l' << 40, + (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'h' << 24, + (long)'u' << 56 | (long)'p' << 48 | (long)'s' << 40 | (long)'i' << 32 | (long)'l' << 24 | (long)'o' << 16 | (long)'n' << 8, + (long)'u' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'w' << 56 | (long)'e' << 48 | (long)'i' << 40 | (long)'e' << 32 | (long)'r' << 24 | (long)'p' << 16, + (long)'x' << 56 | (long)'i' << 48, + (long)'y' << 56 | (long)'a' << 48 | (long)'c' << 40 | (long)'u' << 32 | (long)'t' << 24 | (long)'e' << 16, + (long)'y' << 56 | (long)'e' << 48 | (long)'n' << 40, + (long)'y' << 56 | (long)'u' << 48 | (long)'m' << 40 | (long)'l' << 32, + (long)'z' << 56 | (long)'e' << 48 | (long)'t' << 40 | (long)'a' << 32, + (long)'z' << 56 | (long)'w' << 48 | (long)'j' << 40, + (long)'z' << 56 | (long)'w' << 48 | (long)'n' << 40 | (long)'j' << 32 + }; + + static readonly char[] entities_values = new char[] { + '\u00C6', + '\u00C1', + '\u00C2', + '\u00C0', + '\u0391', + '\u00C5', + '\u00C3', + '\u00C4', + '\u0392', + '\u00C7', + '\u03A7', + '\u2021', + '\u0394', + '\u00D0', + '\u00C9', + '\u00CA', + '\u00C8', + '\u0395', + '\u0397', + '\u00CB', + '\u0393', + '\u00CD', + '\u00CE', + '\u00CC', + '\u0399', + '\u00CF', + '\u039A', + '\u039B', + '\u039C', + '\u00D1', + '\u039D', + '\u0152', + '\u00D3', + '\u00D4', + '\u00D2', + '\u03A9', + '\u039F', + '\u00D8', + '\u00D5', + '\u00D6', + '\u03A6', + '\u03A0', + '\u2033', + '\u03A8', + '\u03A1', + '\u0160', + '\u03A3', + '\u00DE', + '\u03A4', + '\u0398', + '\u00DA', + '\u00DB', + '\u00D9', + '\u03A5', + '\u00DC', + '\u039E', + '\u00DD', + '\u0178', + '\u0396', + '\u00E1', + '\u00E2', + '\u00B4', + '\u00E6', + '\u00E0', + '\u2135', + '\u03B1', + '\u0026', + '\u2227', + '\u2220', + '\u0027', + '\u00E5', + '\u2248', + '\u00E3', + '\u00E4', + '\u201E', + '\u03B2', + '\u00A6', + '\u2022', + '\u2229', + '\u00E7', + '\u00B8', + '\u00A2', + '\u03C7', + '\u02C6', + '\u2663', + '\u2245', + '\u00A9', + '\u21B5', + '\u222A', + '\u00A4', + '\u21D3', + '\u2020', + '\u2193', + '\u00B0', + '\u03B4', + '\u2666', + '\u00F7', + '\u00E9', + '\u00EA', + '\u00E8', + '\u2205', + '\u2003', + '\u2002', + '\u03B5', + '\u2261', + '\u03B7', + '\u00F0', + '\u00EB', + '\u20AC', + '\u2203', + '\u0192', + '\u2200', + '\u00BD', + '\u00BC', + '\u00BE', + '\u2044', + '\u03B3', + '\u2265', + '\u003E', + '\u21D4', + '\u2194', + '\u2665', + '\u2026', + '\u00ED', + '\u00EE', + '\u00A1', + '\u00EC', + '\u2111', + '\u221E', + '\u222B', + '\u03B9', + '\u00BF', + '\u2208', + '\u00EF', + '\u03BA', + '\u21D0', + '\u03BB', + '\u2329', + '\u00AB', + '\u2190', + '\u2308', + '\u201C', + '\u2264', + '\u230A', + '\u2217', + '\u25CA', + '\u200E', + '\u2039', + '\u2018', + '\u003C', + '\u00AF', + '\u2014', + '\u00B5', + '\u00B7', + '\u2212', + '\u03BC', + '\u2207', + '\u00A0', + '\u2013', + '\u2260', + '\u220B', + '\u00AC', + '\u2209', + '\u2284', + '\u00F1', + '\u03BD', + '\u00F3', + '\u00F4', + '\u0153', + '\u00F2', + '\u203E', + '\u03C9', + '\u03BF', + '\u2295', + '\u2228', + '\u00AA', + '\u00BA', + '\u00F8', + '\u00F5', + '\u2297', + '\u00F6', + '\u00B6', + '\u2202', + '\u2030', + '\u22A5', + '\u03C6', + '\u03C0', + '\u03D6', + '\u00B1', + '\u00A3', + '\u2032', + '\u220F', + '\u221D', + '\u03C8', + '\u0022', + '\u21D2', + '\u221A', + '\u232A', + '\u00BB', + '\u2192', + '\u2309', + '\u201D', + '\u211C', + '\u00AE', + '\u230B', + '\u03C1', + '\u200F', + '\u203A', + '\u2019', + '\u201A', + '\u0161', + '\u22C5', + '\u00A7', + '\u00AD', + '\u03C3', + '\u03C2', + '\u223C', + '\u2660', + '\u2282', + '\u2286', + '\u2211', + '\u2283', + '\u00B9', + '\u00B2', + '\u00B3', + '\u2287', + '\u00DF', + '\u03C4', + '\u2234', + '\u03B8', + '\u03D1', + '\u2009', + '\u00FE', + '\u02DC', + '\u00D7', + '\u2122', + '\u21D1', + '\u00FA', + '\u2191', + '\u00FB', + '\u00F9', + '\u00A8', + '\u03D2', + '\u03C5', + '\u00FC', + '\u2118', + '\u03BE', + '\u00FD', + '\u00A5', + '\u00FF', + '\u03B6', + '\u200D', + '\u200C' + }; + + #region Methods + + static void WriteCharBytes(IList buf, char ch, Encoding e) + { + if (ch > 255) + { + foreach (byte b in e.GetBytes(new char[] { ch })) + buf.Add(b); + } + else + buf.Add((byte)ch); + } + + public static string UrlDecode(string s, Encoding e) + { + if (null == s) + return null; + + if (s.IndexOf('%') == -1 && s.IndexOf('+') == -1) + return s; + + if (e == null) + e = Encoding.UTF8; + + long len = s.Length; + var bytes = new List<byte>(); + int xchar; + char ch; + + for (int i = 0; i < len; i++) + { + ch = s[i]; + if (ch == '%' && i + 2 < len && s[i + 1] != '%') + { + if (s[i + 1] == 'u' && i + 5 < len) + { + // unicode hex sequence + xchar = GetChar(s, i + 2, 4); + if (xchar != -1) + { + WriteCharBytes(bytes, (char)xchar, e); + i += 5; + } + else + WriteCharBytes(bytes, '%', e); + } + else if ((xchar = GetChar(s, i + 1, 2)) != -1) + { + WriteCharBytes(bytes, (char)xchar, e); + i += 2; + } + else + { + WriteCharBytes(bytes, '%', e); + } + continue; + } + + if (ch == '+') + WriteCharBytes(bytes, ' ', e); + else + WriteCharBytes(bytes, ch, e); + } + + byte[] buf = bytes.ToArray(); + bytes = null; + return e.GetString(buf); + + } + + static int GetInt(byte b) + { + char c = (char)b; + if (c >= '0' && c <= '9') + return c - '0'; + + if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + + if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + + return -1; + } + + static int GetChar(string str, int offset, int length) + { + int val = 0; + int end = length + offset; + for (int i = offset; i < end; i++) + { + char c = str[i]; + if (c > 127) + return -1; + + int current = GetInt((byte)c); + if (current == -1) + return -1; + val = (val << 4) + current; + } + + return val; + } + + static bool TryConvertKeyToEntity(string key, out char value) + { + var token = CalculateKeyValue(key); + if (token == 0) + { + value = '\0'; + return false; + } + + var idx = Array.BinarySearch(entities, token); + if (idx < 0) + { + value = '\0'; + return false; + } + + value = entities_values[idx]; + return true; + } + + static long CalculateKeyValue(string s) + { + if (s.Length > 8) + return 0; + + long key = 0; + for (int i = 0; i < s.Length; ++i) + { + long ch = s[i]; + if (ch > 'z' || ch < '0') + return 0; + + key |= ch << ((7 - i) * 8); + } + + return key; + } + + /// <summary> + /// Decodes an HTML-encoded string and returns the decoded string. + /// </summary> + /// <param name="s">The HTML string to decode. </param> + /// <returns>The decoded text.</returns> + public static string HtmlDecode(string s) + { + if (s == null) + throw new ArgumentNullException("s"); + + if (s.IndexOf('&') == -1) + return s; + + StringBuilder entity = new StringBuilder(); + StringBuilder output = new StringBuilder(); + int len = s.Length; + // 0 -> nothing, + // 1 -> right after '&' + // 2 -> between '&' and ';' but no '#' + // 3 -> '#' found after '&' and getting numbers + int state = 0; + int number = 0; + int digit_start = 0; + bool hex_number = false; + + for (int i = 0; i < len; i++) + { + char c = s[i]; + if (state == 0) + { + if (c == '&') + { + entity.Append(c); + state = 1; + } + else + { + output.Append(c); + } + continue; + } + + if (c == '&') + { + state = 1; + if (digit_start > 0) + { + entity.Append(s, digit_start, i - digit_start); + digit_start = 0; + } + + output.Append(entity.ToString()); + entity.Length = 0; + entity.Append('&'); + continue; + } + + switch (state) + { + case 1: + if (c == ';') + { + state = 0; + output.Append(entity.ToString()); + output.Append(c); + entity.Length = 0; + break; + } + + number = 0; + hex_number = false; + if (c != '#') + { + state = 2; + } + else + { + state = 3; + } + entity.Append(c); + + break; + case 2: + entity.Append(c); + if (c == ';') + { + string key = entity.ToString(); + state = 0; + entity.Length = 0; + + if (key.Length > 1) + { + var skey = key.Substring(1, key.Length - 2); + if (TryConvertKeyToEntity(skey, out c)) + { + output.Append(c); + break; + } + } + + output.Append(key); + } + + break; + case 3: + if (c == ';') + { + if (number < 0x10000) + { + output.Append((char)number); + } + else + { + output.Append((char)(0xd800 + ((number - 0x10000) >> 10))); + output.Append((char)(0xdc00 + ((number - 0x10000) & 0x3ff))); + } + state = 0; + entity.Length = 0; + digit_start = 0; + break; + } + + if (c == 'x' || c == 'X' && !hex_number) + { + digit_start = i; + hex_number = true; + break; + } + + if (Char.IsDigit(c)) + { + if (digit_start == 0) + digit_start = i; + + number = number * (hex_number ? 16 : 10) + ((int)c - '0'); + break; + } + + if (hex_number) + { + if (c >= 'a' && c <= 'f') + { + number = number * 16 + 10 + ((int)c - 'a'); + break; + } + if (c >= 'A' && c <= 'F') + { + number = number * 16 + 10 + ((int)c - 'A'); + break; + } + } + + state = 2; + if (digit_start > 0) + { + entity.Append(s, digit_start, i - digit_start); + digit_start = 0; + } + + entity.Append(c); + break; + } + } + + if (entity.Length > 0) + { + output.Append(entity); + } + else if (digit_start > 0) + { + output.Append(s, digit_start, s.Length - digit_start); + } + return output.ToString(); + } + + public static NameValueCollection ParseQueryString(string query) + { + return ParseQueryString(query, Encoding.UTF8); + } + + public static NameValueCollection ParseQueryString(string query, Encoding encoding) + { + if (query == null) + throw new ArgumentNullException("query"); + if (encoding == null) + throw new ArgumentNullException("encoding"); + if (query.Length == 0 || (query.Length == 1 && query[0] == '?')) + return new NameValueCollection(); + if (query[0] == '?') + query = query.Substring(1); + + NameValueCollection result = new HttpQSCollection(); + ParseQueryString(query, encoding, result); + return result; + } + + internal static void ParseQueryString(string query, Encoding encoding, NameValueCollection result) + { + if (query.Length == 0) + return; + + string decoded = HtmlDecode(query); + int decodedLength = decoded.Length; + int namePos = 0; + bool first = true; + while (namePos <= decodedLength) + { + int valuePos = -1, valueEnd = -1; + for (int q = namePos; q < decodedLength; q++) + { + if (valuePos == -1 && decoded[q] == '=') + { + valuePos = q + 1; + } + else if (decoded[q] == '&') + { + valueEnd = q; + break; + } + } + + if (first) + { + first = false; + if (decoded[namePos] == '?') + namePos++; + } + + string name, value; + if (valuePos == -1) + { + name = null; + valuePos = namePos; + } + else + { + name = UrlDecode(decoded.Substring(namePos, valuePos - namePos - 1), encoding); + } + if (valueEnd < 0) + { + namePos = -1; + valueEnd = decoded.Length; + } + else + { + namePos = valueEnd + 1; + } + value = UrlDecode(decoded.Substring(valuePos, valueEnd - valuePos), encoding); + + result.Add(name, value); + if (namePos == -1) + break; + } + } + #endregion // Methods + } +} diff --git a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs index 54c27cf0a..b3fbd2d1d 100644 --- a/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs +++ b/MediaBrowser.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpRequest.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Text; +using System.Web; using Funq; using MediaBrowser.Model.Logging; using ServiceStack; @@ -236,7 +237,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer.SocketSharp private NameValueCollectionWrapper queryString; public INameValueCollection QueryString { - get { return queryString ?? (queryString = new NameValueCollectionWrapper(HttpUtility.ParseQueryString(request.Url.Query))); } + get { return queryString ?? (queryString = new NameValueCollectionWrapper(MyHttpUtility.ParseQueryString(request.Url.Query))); } } private NameValueCollectionWrapper formData; 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 37131ad2c..edc329ec4 100644 --- a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs +++ b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -78,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 => @@ -163,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; @@ -212,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 { @@ -230,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) @@ -341,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/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..63d1d4cb8 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) @@ -163,16 +124,7 @@ namespace MediaBrowser.Server.Implementations.Library var channels = channelResult.Items; - var embeddedChannels = channels - .Where(i => user.Configuration.DisplayChannelsInline || user.Configuration.DisplayChannelsWithinViews.Contains(i.Id.ToString("N"))) - .ToList(); - - list.AddRange(embeddedChannels); - - if (channels.Length > embeddedChannels.Count) - { - list.Add(await _channelManager.GetInternalChannelFolder(cancellationToken).ConfigureAwait(false)); - } + list.AddRange(channels); if (_liveTvManager.GetEnabledUsers().Select(i => i.Id.ToString("N")).Contains(query.UserId)) { @@ -189,6 +141,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 +173,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) + public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, 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) - { - 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 +202,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 +213,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 +250,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..f8b4e71cf 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)) { @@ -1516,6 +1514,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv if (channel != null) { dto.ChannelName = channel.Name; + dto.MediaType = channel.MediaType; if (channel.HasImage(ImageType.Primary)) { @@ -1812,7 +1811,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 +1820,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv Limit = 1, SortBy = new[] { "StartDate" } - }).Items.Cast<LiveTvProgram>(); + }, new string[] { }).Cast<LiveTvProgram>(); var currentProgram = programs.FirstOrDefault(); @@ -1836,7 +1835,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 +1844,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/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index fb8e77006..0671a9b56 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -252,7 +252,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun height = 1080; isInterlaced = false; videoCodec = "h264"; - videoBitrate = 8000000; + videoBitrate = 15000000; } else if (string.Equals(profile, "internet720", StringComparison.OrdinalIgnoreCase)) { @@ -260,7 +260,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun height = 720; isInterlaced = false; videoCodec = "h264"; - videoBitrate = 5000000; + videoBitrate = 8000000; } else if (string.Equals(profile, "internet540", StringComparison.OrdinalIgnoreCase)) { @@ -326,12 +326,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv.TunerHosts.HdHomerun // Set the index to -1 because we don't know the exact index of the audio stream within the container Index = -1, Codec = "ac3", - BitRate = 128000 + BitRate = 192000 } }, RequiresOpening = false, RequiresClosing = false, - BufferMs = 1000, + BufferMs = 0, Container = "ts", Id = profile, SupportsDirectPlay = true, diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index f488f443e..52adae14c 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -52,15 +52,18 @@ <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"> + <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.5840.28948, 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.25\lib\net45\SocketHttpListener.dll</HintPath> </Reference> <Reference Include="System" /> <Reference Include="System.Core" /> @@ -70,6 +73,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 +98,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"> @@ -160,6 +161,7 @@ <Compile Include="HttpServer\ServerLogFactory.cs" /> <Compile Include="HttpServer\ServerLogger.cs" /> <Compile Include="HttpServer\Security\SessionContext.cs" /> + <Compile Include="HttpServer\SocketSharp\HttpUtility.cs" /> <Compile Include="HttpServer\SocketSharp\SharpWebSocket.cs" /> <Compile Include="HttpServer\StreamWriter.cs" /> <Compile Include="HttpServer\SwaggerService.cs" /> @@ -216,9 +218,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" /> @@ -408,6 +407,7 @@ <EmbeddedResource Include="Localization\Core\zh-CN.json" /> <EmbeddedResource Include="Localization\Core\zh-TW.json" /> <EmbeddedResource Include="Localization\Core\zh-HK.json" /> + <EmbeddedResource Include="Localization\Core\hu.json" /> <None Include="packages.config" /> </ItemGroup> <ItemGroup> diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index bd634ed73..ac0f2c69d 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -124,8 +124,7 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder { if (extractImages) { - if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || - video.VideoType == VideoType.BluRay) + if (video.VideoType == VideoType.HdDvd || video.VideoType == VideoType.Iso || video.VideoType == VideoType.BluRay) { continue; } 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 dc6241362..1d3ac293e 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, 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,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "IsAnamorphic", "RefFrames", "IsCabac", - "KeyFrames" + "CodecTag" }; /// <summary> @@ -378,6 +425,7 @@ namespace MediaBrowser.Server.Implementations.Persistence "ParentId", "Genres", "ParentalRatingValue", + "InheritedParentalRatingValue", "SchemaVersion", "SortName", "RunTimeTicks", @@ -394,7 +442,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 +496,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 +536,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 +666,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 +698,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 +825,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 +973,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 +1398,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 +1445,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 +1569,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 +1610,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 +1794,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 +1859,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 +1916,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 +1924,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 +1939,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 +2049,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 +2135,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 +2195,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 +2433,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) @@ -2220,7 +2682,7 @@ namespace MediaBrowser.Server.Implementations.Persistence _saveStreamCommand.GetParameter(index++).Value = stream.RefFrames; _saveStreamCommand.GetParameter(index++).Value = stream.IsCabac; - _saveStreamCommand.GetParameter(index++).Value = null; + _saveStreamCommand.GetParameter(index++).Value = stream.CodecTag; _saveStreamCommand.Transaction = transaction; _saveStreamCommand.ExecuteNonQuery(); @@ -2376,10 +2838,7 @@ namespace MediaBrowser.Server.Implementations.Persistence if (!reader.IsDBNull(26)) { - var frames = reader.GetString(26); - if (!string.IsNullOrWhiteSpace(frames)) - { - } + item.CodecTag = reader.GetString(26); } 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..aac5951b5 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.25" targetFramework="net45" />
</packages>
\ No newline at end of file diff --git a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs index ba6d7ad1f..8f3006e11 100644 --- a/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs +++ b/MediaBrowser.Server.Mono/Native/BaseMonoApp.cs @@ -119,7 +119,7 @@ namespace MediaBrowser.Server.Mono.Native { get { - return false; + return Environment.OperatingSystem == Startup.Common.OperatingSystem.Linux; } } diff --git a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs index 99a7f0a15..2bc01ebe1 100644 --- a/MediaBrowser.Server.Startup.Common/ApplicationHost.cs +++ b/MediaBrowser.Server.Startup.Common/ApplicationHost.cs @@ -91,10 +91,12 @@ using MediaBrowser.Server.Startup.Common.Migrations; using MediaBrowser.WebDashboard.Api; using MediaBrowser.XbmcMetadata.Providers; using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; +using System.Net; using System.Reflection; using System.Threading; using System.Threading.Tasks; @@ -207,6 +209,7 @@ namespace MediaBrowser.Server.Startup.Common private readonly string _remotePackageName; internal INativeApp NativeApp { get; set; } + private Timer _ipAddressCacheTimer; /// <summary> /// Initializes a new instance of the <see cref="ApplicationHost" /> class. @@ -230,6 +233,8 @@ namespace MediaBrowser.Server.Startup.Common NativeApp = nativeApp; SetBaseExceptionMessage(); + + _ipAddressCacheTimer = new Timer(OnCacheClearTimerFired, null, TimeSpan.FromMinutes(3), TimeSpan.FromMinutes(3)); } private Version _version; @@ -316,6 +321,7 @@ namespace MediaBrowser.Server.Startup.Common { await base.RunStartupTasks().ConfigureAwait(false); + Logger.Info("ServerId: {0}", SystemId); Logger.Info("Core startup complete"); HttpServer.GlobalResponse = null; @@ -363,7 +369,7 @@ namespace MediaBrowser.Server.Startup.Common { var migrations = new List<IVersionMigration> { - new Release5767(ServerConfigurationManager, TaskManager) + new DbMigration(ServerConfigurationManager, TaskManager) }; foreach (var task in migrations) @@ -419,7 +425,7 @@ namespace MediaBrowser.Server.Startup.Common UserManager = new UserManager(LogManager.GetLogger("UserManager"), ServerConfigurationManager, UserRepository, XmlSerializer, NetworkManager, () => ImageProcessor, () => DtoService, () => ConnectManager, this, JsonSerializer, FileSystemManager); RegisterSingleInstance(UserManager); - LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager); + LibraryManager = new LibraryManager(Logger, TaskManager, UserManager, ServerConfigurationManager, UserDataManager, () => LibraryMonitor, FileSystemManager, () => ProviderManager, () => UserViewManager); RegisterSingleInstance(LibraryManager); var musicManager = new MusicManager(LibraryManager); @@ -436,7 +442,7 @@ namespace MediaBrowser.Server.Startup.Common RegisterSingleInstance<ISearchEngine>(() => new SearchEngine(LogManager, LibraryManager, UserManager)); - HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, "Emby", "web/index.html"); + HttpServer = ServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, "Emby", "web/index.html"); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); @@ -465,7 +471,7 @@ namespace MediaBrowser.Server.Startup.Common ConnectManager = new ConnectManager(LogManager.GetLogger("Connect"), ApplicationPaths, JsonSerializer, encryptionManager, HttpClient, this, ServerConfigurationManager, UserManager, ProviderManager, SecurityManager, FileSystemManager); RegisterSingleInstance(ConnectManager); - DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); + DeviceManager = new DeviceManager(new DeviceRepository(ApplicationPaths, JsonSerializer, LogManager.GetLogger("DeviceManager"), FileSystemManager), UserManager, FileSystemManager, LibraryMonitor, ServerConfigurationManager, LogManager.GetLogger("DeviceManager"), NetworkManager); RegisterSingleInstance(DeviceManager); var newsService = new Implementations.News.NewsService(ApplicationPaths, JsonSerializer); @@ -503,7 +509,7 @@ namespace MediaBrowser.Server.Startup.Common UserViewManager = new UserViewManager(LibraryManager, LocalizationManager, UserManager, ChannelManager, LiveTvManager, ServerConfigurationManager); RegisterSingleInstance(UserViewManager); - var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager); + var contentDirectory = new ContentDirectory(dlnaManager, UserDataManager, ImageProcessor, LibraryManager, ServerConfigurationManager, UserManager, LogManager.GetLogger("UpnpContentDirectory"), HttpClient, LocalizationManager, ChannelManager, MediaSourceManager, UserViewManager); RegisterSingleInstance<IContentDirectory>(contentDirectory); var mediaRegistrar = new MediaReceiverRegistrar(LogManager.GetLogger("MediaReceiverRegistrar"), HttpClient, ServerConfigurationManager); @@ -640,11 +646,19 @@ namespace MediaBrowser.Server.Startup.Common /// <returns>Task{IUserRepository}.</returns> private async Task<IUserRepository> GetUserRepository() { - var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer); + try + { + var repo = new SqliteUserRepository(LogManager, ApplicationPaths, JsonSerializer); - await repo.Initialize().ConfigureAwait(false); + await repo.Initialize().ConfigureAwait(false); - return repo; + return repo; + } + catch (Exception ex) + { + Logger.ErrorException("Error opening user db", ex); + throw; + } } /// <summary> @@ -1108,14 +1122,14 @@ namespace MediaBrowser.Server.Startup.Common try { // Return the first matched address, if found, or the first known local address - var address = LocalIpAddress; + var address = LocalIpAddresses.FirstOrDefault(i => !IPAddress.IsLoopback(i)); - if (!string.IsNullOrWhiteSpace(address)) + if (address != null) { - address = GetLocalApiUrl(address); + return GetLocalApiUrl(address.ToString()); } - return address; + return null; } catch (Exception ex) { @@ -1133,40 +1147,71 @@ namespace MediaBrowser.Server.Startup.Common HttpPort.ToString(CultureInfo.InvariantCulture)); } - public string LocalIpAddress + public List<IPAddress> LocalIpAddresses { get { - return HttpServerIpAddresses.FirstOrDefault(); + var localAddresses = NetworkManager.GetLocalIpAddresses() + .Where(IsIpAddressValid) + .ToList(); + + return localAddresses; } } - private IEnumerable<string> HttpServerIpAddresses + private readonly ConcurrentDictionary<string, bool> _validAddressResults = new ConcurrentDictionary<string, bool>(StringComparer.OrdinalIgnoreCase); + private bool IsIpAddressValid(IPAddress address) { - get + if (IPAddress.IsLoopback(address)) { - var localAddresses = NetworkManager.GetLocalIpAddresses() - .ToList(); + return true; + } - var httpServerAddresses = HttpServer.LocalEndPoints - .Select(i => i.Split(':').FirstOrDefault()) - .Where(i => !string.IsNullOrEmpty(i)) - .ToList(); + var apiUrl = GetLocalApiUrl(address.ToString()); + apiUrl += "/system/ping"; - // Cross-check the local ip addresses with addresses that have been received on with the http server - var matchedAddresses = httpServerAddresses - .Where(i => localAddresses.Contains(i, StringComparer.OrdinalIgnoreCase)) - .ToList(); + bool cachedResult; + if (_validAddressResults.TryGetValue(apiUrl, out cachedResult)) + { + return cachedResult; + } - if (matchedAddresses.Count == 0) + try + { + using (var response = HttpClient.SendAsync(new HttpRequestOptions + { + Url = apiUrl, + LogErrorResponseBody = false, + LogErrors = false, + LogRequest = false + + }, "POST").Result) { - return localAddresses; + using (var reader = new StreamReader(response.Content)) + { + var result = reader.ReadToEnd(); + var valid = string.Equals(Name, result, StringComparison.OrdinalIgnoreCase); + + _validAddressResults.AddOrUpdate(apiUrl, valid, (k, v) => valid); + Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, valid); + return valid; + } } + } + catch + { + Logger.Debug("Ping test result to {0}. Success: {1}", apiUrl, false); - return matchedAddresses; + _validAddressResults.AddOrUpdate(apiUrl, false, (k, v) => false); + return false; } } + private void OnCacheClearTimerFired(object state) + { + _validAddressResults.Clear(); + } + public string FriendlyName { get diff --git a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj index 13b782e40..b58646a2e 100644 --- a/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj +++ b/MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj @@ -72,7 +72,7 @@ <Compile Include="INativeApp.cs" /> <Compile Include="MbLinkShortcutHandler.cs" /> <Compile Include="Migrations\IVersionMigration.cs" /> - <Compile Include="Migrations\Release5767.cs" /> + <Compile Include="Migrations\DbMigration.cs" /> <Compile Include="Migrations\RenameXmlOptions.cs" /> <Compile Include="NativeEnvironment.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> diff --git a/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs b/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs new file mode 100644 index 000000000..cdb69025a --- /dev/null +++ b/MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Server.Implementations.Persistence; + +namespace MediaBrowser.Server.Startup.Common.Migrations +{ + public class DbMigration : IVersionMigration + { + private readonly IServerConfigurationManager _config; + private readonly ITaskManager _taskManager; + + public DbMigration(IServerConfigurationManager config, ITaskManager taskManager) + { + _config = config; + _taskManager = taskManager; + } + + public void Run() + { + if (_config.Configuration.MigrationVersion < CleanDatabaseScheduledTask.MigrationVersion) + { + Task.Run(async () => + { + await Task.Delay(2000).ConfigureAwait(false); + + _taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>(); + }); + } + } + } +} diff --git a/MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs b/MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs deleted file mode 100644 index 168230b87..000000000 --- a/MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Server.Implementations.LiveTv; -using MediaBrowser.Server.Implementations.Persistence; -using MediaBrowser.Server.Implementations.ScheduledTasks; - -namespace MediaBrowser.Server.Startup.Common.Migrations -{ - public class Release5767 : IVersionMigration - { - private readonly IServerConfigurationManager _config; - private readonly ITaskManager _taskManager; - - public Release5767(IServerConfigurationManager config, ITaskManager taskManager) - { - _config = config; - _taskManager = taskManager; - } - - public async void Run() - { - var name = "5767.1"; - - if (_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase)) - { - return; - } - - Task.Run(async () => - { - await Task.Delay(3000).ConfigureAwait(false); - - _taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>(); - }); - - // Wait a few minutes before marking this as done. Make sure the server doesn't get restarted. - await Task.Delay(300000).ConfigureAwait(false); - - var list = _config.Configuration.Migrations.ToList(); - list.Add(name); - _config.Configuration.Migrations = list.ToArray(); - _config.SaveConfiguration(); - } - } -} diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs index b8af35fde..3fec815f5 100644 --- a/MediaBrowser.ServerApplication/MainStartup.cs +++ b/MediaBrowser.ServerApplication/MainStartup.cs @@ -18,6 +18,7 @@ using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using CommonIO.Windows; +using ImageMagickSharp; using MediaBrowser.Server.Implementations.Logging; namespace MediaBrowser.ServerApplication @@ -29,6 +30,10 @@ namespace MediaBrowser.ServerApplication private static ILogger _logger; private static bool _isRunningAsService = false; + private static bool _appHostDisposed; + + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool SetDllDirectory(string lpPathName); /// <summary> /// Defines the entry point of the application. @@ -41,6 +46,11 @@ namespace MediaBrowser.ServerApplication var currentProcess = Process.GetCurrentProcess(); var applicationPath = currentProcess.MainModule.FileName; + var architecturePath = Path.Combine(Path.GetDirectoryName(applicationPath), Environment.Is64BitProcess ? "x64" : "x86"); + + Wand.SetMagickCoderModulePath(architecturePath); + + var success = SetDllDirectory(architecturePath); var appPaths = CreateApplicationPaths(applicationPath, _isRunningAsService); @@ -329,7 +339,7 @@ namespace MediaBrowser.ServerApplication { _logger.Info("Shutting down"); - _appHost.Dispose(); + DisposeAppHost(); } /// <summary> @@ -500,14 +510,15 @@ namespace MediaBrowser.ServerApplication } else { + DisposeAppHost(); + ShutdownWindowsApplication(); } } public static void Restart() { - _logger.Info("Disposing app host"); - _appHost.Dispose(); + DisposeAppHost(); if (!_isRunningAsService) { @@ -522,11 +533,24 @@ namespace MediaBrowser.ServerApplication } } + private static void DisposeAppHost() + { + if (!_appHostDisposed) + { + _logger.Info("Disposing app host"); + + _appHostDisposed = true; + _appHost.Dispose(); + } + } + private static void ShutdownWindowsApplication() { _logger.Info("Calling Application.Exit"); Application.Exit(); + Environment.Exit(0); + _logger.Info("Calling ApplicationTaskCompletionSource.SetResult"); ApplicationTaskCompletionSource.SetResult(true); } diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj index 5813dcac5..5048cecdc 100644 --- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj +++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj @@ -22,7 +22,7 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <Prefer32Bit>true</Prefer32Bit> + <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <PlatformTarget>AnyCPU</PlatformTarget> @@ -32,6 +32,7 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> + <Prefer32Bit>false</Prefer32Bit> </PropertyGroup> <PropertyGroup> <StartupObject>MediaBrowser.ServerApplication.MainStartup</StartupObject> @@ -64,8 +65,9 @@ <SpecificVersion>False</SpecificVersion> <HintPath>..\packages\CommonIO.1.0.0.5\lib\net45\CommonIO.dll</HintPath> </Reference> - <Reference Include="ImageMagickSharp"> - <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath> + <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> + <SpecificVersion>False</SpecificVersion> + <HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll</HintPath> </Reference> <Reference Include="MediaBrowser.IsoMounter"> <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.69\lib\net45\MediaBrowser.IsoMounter.dll</HintPath> @@ -176,230 +178,878 @@ <Link>x86\SQLite.Interop.dll</Link> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_bzlib_.dll"> + <Content Include="x64\CORE_RL_bzlib_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_cairo_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_exr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_glib_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_jp2_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_jpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_lcms_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_librsvg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_libxml_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_lqr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_Magick++_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_magick_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_openjpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_pango_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_png_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_tiff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_ttf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_wand_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_webp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\CORE_RL_zlib_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_aai_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_art_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_avs_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_bgr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_bmp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_braille_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_cals_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_caption_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_cin_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_cip_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_clipboard_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_clip_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_cmyk_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_cut_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dcm_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dds_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_debug_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dib_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_djvu_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dng_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dot_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dps_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_dpx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_emf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ept_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_exr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_fax_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_fd_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_fits_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_fpx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_gif_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_gradient_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_gray_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_hald_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_hdr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_histogram_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_hrz_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_html_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_icon_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_info_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_inline_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ipl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_jbig_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_jnx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_jp2_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_jpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_json_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_label_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mac_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_magick_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_map_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mask_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_matte_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mat_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_meta_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_miff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mono_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mpc_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mpr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_msl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mtv_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_mvg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_null_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_otb_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_palm_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pango_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pattern_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pcd_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pcl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pcx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pdb_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pdf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pes_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pict_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pix_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_plasma_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_png_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pnm_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_preview_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ps2_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ps3_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_psd_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ps_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_pwp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_raw_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_rgb_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_rgf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_rla_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_rle_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_screenshot_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_scr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_sct_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_sfw_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_sgi_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_sixel_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_stegano_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_sun_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_svg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_tga_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_thumbnail_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_tiff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_tile_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_tim_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_ttf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_txt_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_uil_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_url_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_uyvy_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_vicar_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_vid_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_viff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_vips_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_wbmp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_webp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x64\IM_MOD_RL_wmf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_cairo_.dll"> + <Content Include="x64\IM_MOD_RL_wpg_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_exr_.dll"> + <Content Include="x64\IM_MOD_RL_xbm_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_glib_.dll"> + <Content Include="x64\IM_MOD_RL_xcf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_jbig_.dll"> + <Content Include="x64\IM_MOD_RL_xc_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_jp2_.dll"> + <Content Include="x64\IM_MOD_RL_xpm_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_jpeg_.dll"> + <Content Include="x64\IM_MOD_RL_xps_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_lcms_.dll"> + <Content Include="x64\IM_MOD_RL_xtrn_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_librsvg_.dll"> + <Content Include="x64\IM_MOD_RL_ycbcr_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_libxml_.dll"> + <Content Include="x64\IM_MOD_RL_yuv_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_lqr_.dll"> + <Content Include="x86\CORE_RL_bzlib_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_Magick++_.dll"> + <Content Include="x86\CORE_RL_cairo_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_magick_.dll"> + <Content Include="x86\CORE_RL_exr_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_openjpeg_.dll"> + <Content Include="x86\CORE_RL_glib_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_pango_.dll"> + <Content Include="x86\CORE_RL_jbig_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_png_.dll"> + <Content Include="x86\CORE_RL_jp2_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_tiff_.dll"> + <Content Include="x86\CORE_RL_jpeg_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_ttf_.dll"> + <Content Include="x86\CORE_RL_lcms_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_wand_.dll"> + <Content Include="x86\CORE_RL_librsvg_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_webp_.dll"> + <Content Include="x86\CORE_RL_libxml_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="CORE_RL_zlib_.dll"> + <Content Include="x86\CORE_RL_lqr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_Magick++_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_magick_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_openjpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_pango_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_png_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_tiff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_ttf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_wand_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_webp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\CORE_RL_zlib_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <Content Include="Icon.ico" /> - <Content Include="IM_MOD_RL_bgr_.dll"> + <Content Include="x86\IM_MOD_RL_aai_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_art_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_avs_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_bgr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_bmp_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_braille_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_cals_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_caption_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_cin_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_cip_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_clipboard_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_clip_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_cmyk_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_cut_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dcm_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dds_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_debug_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dib_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_djvu_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dng_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dot_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dps_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_dpx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_emf_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_ept_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_exr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_fax_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_fd_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_fits_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_fpx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_gif_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_gradient_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_gray_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_hald_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_hdr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_histogram_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_hrz_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_html_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_icon_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_info_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_inline_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_ipl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_jbig_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_jnx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_jp2_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_jpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_json_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_label_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mac_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_magick_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_map_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mask_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_matte_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mat_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_meta_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_miff_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mono_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mpc_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mpeg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mpr_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_msl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mtv_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_mvg_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_null_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_otb_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_palm_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pango_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pattern_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pcd_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pcl_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pcx_.dll"> + <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> + </Content> + <Content Include="x86\IM_MOD_RL_pdb_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_bmp_.dll"> + <Content Include="x86\IM_MOD_RL_pdf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_caption_.dll"> + <Content Include="x86\IM_MOD_RL_pes_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_clipboard_.dll"> + <Content Include="x86\IM_MOD_RL_pict_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_clip_.dll"> + <Content Include="x86\IM_MOD_RL_pix_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_cmyk_.dll"> + <Content Include="x86\IM_MOD_RL_plasma_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_cut_.dll"> + <Content Include="x86\IM_MOD_RL_png_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_dib_.dll"> + <Content Include="x86\IM_MOD_RL_pnm_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_emf_.dll"> + <Content Include="x86\IM_MOD_RL_preview_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_gif_.dll"> + <Content Include="x86\IM_MOD_RL_ps2_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_gradient_.dll"> + <Content Include="x86\IM_MOD_RL_ps3_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_gray_.dll"> + <Content Include="x86\IM_MOD_RL_psd_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_hdr_.dll"> + <Content Include="x86\IM_MOD_RL_ps_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_histogram_.dll"> + <Content Include="x86\IM_MOD_RL_pwp_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_html_.dll"> + <Content Include="x86\IM_MOD_RL_raw_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_icon_.dll"> + <Content Include="x86\IM_MOD_RL_rgb_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_info_.dll"> + <Content Include="x86\IM_MOD_RL_rgf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_jpeg_.dll"> + <Content Include="x86\IM_MOD_RL_rla_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_json_.dll"> + <Content Include="x86\IM_MOD_RL_rle_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_label_.dll"> + <Content Include="x86\IM_MOD_RL_screenshot_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_magick_.dll"> + <Content Include="x86\IM_MOD_RL_scr_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_map_.dll"> + <Content Include="x86\IM_MOD_RL_sct_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_mask_.dll"> + <Content Include="x86\IM_MOD_RL_sfw_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_matte_.dll"> + <Content Include="x86\IM_MOD_RL_sgi_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_mat_.dll"> + <Content Include="x86\IM_MOD_RL_sixel_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_meta_.dll"> + <Content Include="x86\IM_MOD_RL_stegano_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_miff_.dll"> + <Content Include="x86\IM_MOD_RL_sun_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_mono_.dll"> + <Content Include="x86\IM_MOD_RL_svg_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_mpeg_.dll"> + <Content Include="x86\IM_MOD_RL_tga_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_null_.dll"> + <Content Include="x86\IM_MOD_RL_thumbnail_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_pattern_.dll"> + <Content Include="x86\IM_MOD_RL_tiff_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_pdf_.dll"> + <Content Include="x86\IM_MOD_RL_tile_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_png_.dll"> + <Content Include="x86\IM_MOD_RL_tim_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_preview_.dll"> + <Content Include="x86\IM_MOD_RL_ttf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_ps2_.dll"> + <Content Include="x86\IM_MOD_RL_txt_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_ps3_.dll"> + <Content Include="x86\IM_MOD_RL_uil_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_psd_.dll"> + <Content Include="x86\IM_MOD_RL_url_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_ps_.dll"> + <Content Include="x86\IM_MOD_RL_uyvy_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_raw_.dll"> + <Content Include="x86\IM_MOD_RL_vicar_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_rgb_.dll"> + <Content Include="x86\IM_MOD_RL_vid_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_screenshot_.dll"> + <Content Include="x86\IM_MOD_RL_viff_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_thumbnail_.dll"> + <Content Include="x86\IM_MOD_RL_vips_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_tiff_.dll"> + <Content Include="x86\IM_MOD_RL_wbmp_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_tile_.dll"> + <Content Include="x86\IM_MOD_RL_webp_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_ttf_.dll"> + <Content Include="x86\IM_MOD_RL_wmf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_txt_.dll"> + <Content Include="x86\IM_MOD_RL_wpg_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_url_.dll"> + <Content Include="x86\IM_MOD_RL_xbm_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_uyvy_.dll"> + <Content Include="x86\IM_MOD_RL_xcf_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_wbmp_.dll"> + <Content Include="x86\IM_MOD_RL_xc_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_webp_.dll"> + <Content Include="x86\IM_MOD_RL_xpm_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_wmf_.dll"> + <Content Include="x86\IM_MOD_RL_xps_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_xc_.dll"> + <Content Include="x86\IM_MOD_RL_xtrn_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_xps_.dll"> + <Content Include="x86\IM_MOD_RL_ycbcr_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> - <Content Include="IM_MOD_RL_yuv_.dll"> + <Content Include="x86\IM_MOD_RL_yuv_.dll"> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> </Content> <Content Include="Resources\Images\mb3logo800.png" /> diff --git a/MediaBrowser.ServerApplication/Native/Autorun.cs b/MediaBrowser.ServerApplication/Native/Autorun.cs index 2cacaae8e..c384d0460 100644 --- a/MediaBrowser.ServerApplication/Native/Autorun.cs +++ b/MediaBrowser.ServerApplication/Native/Autorun.cs @@ -17,25 +17,10 @@ namespace MediaBrowser.ServerApplication.Native /// <param name="fileSystem">The file system.</param> public static void Configure(bool autorun, IFileSystem fileSystem) { - var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Media Browser 3", "Media Browser Server.lnk"); - - if (!Directory.Exists(Path.GetDirectoryName(shortcutPath))) - { - shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk"); - } + var shortcutPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.StartMenu), "Emby", "Emby Server.lnk"); var startupPath = Environment.GetFolderPath(Environment.SpecialFolder.Startup); - // Remove lnk from old name - try - { - fileSystem.DeleteFile(Path.Combine(startupPath, Path.GetFileName(shortcutPath) ?? "Media Browser Server.lnk")); - } - catch - { - - } - if (autorun) { //Copy our shortut into the startup folder for this user diff --git a/MediaBrowser.ServerApplication/packages.config b/MediaBrowser.ServerApplication/packages.config index dec8199cc..3ea97a30e 100644 --- a/MediaBrowser.ServerApplication/packages.config +++ b/MediaBrowser.ServerApplication/packages.config @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="utf-8"?> <packages> <package id="CommonIO" version="1.0.0.5" targetFramework="net45" /> - <package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" /> + <package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" /> <package id="MediaBrowser.IsoMounting" version="3.0.69" targetFramework="net45" /> <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> <package id="System.Data.SQLite.Core" version="1.0.94.0" targetFramework="net45" /> diff --git a/MediaBrowser.sln b/MediaBrowser.sln index d0fbd743e..a9b5020ec 100644 --- a/MediaBrowser.sln +++ b/MediaBrowser.sln @@ -12,6 +12,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Performance2.psess = Performance2.psess Performance3.psess = Performance3.psess Performance4.psess = Performance4.psess + Performance5.psess = Performance5.psess EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget (2)", ".nuget (2)", "{E60FB157-87E2-4A41-8B04-27EA49B63B4D}" @@ -520,4 +521,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(Performance) = preSolution + HasPerformanceSessions = true + EndGlobalSection EndGlobal diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 8fa5b665d..7c59cde06 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.638</version> + <version>3.0.639</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,9 +12,9 @@ <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.638" /> - <dependency id="NLog" version="3.2.1" /> - <dependency id="SimpleInjector" version="3.0.5" /> + <dependency id="MediaBrowser.Common" version="3.0.639" /> + <dependency id="NLog" version="4.2.2" /> + <dependency id="SimpleInjector" version="3.1.2" /> </dependencies> </metadata> <files> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index cec3f63d5..793336fbe 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.638</version> + <version>3.0.639</version> <title>MediaBrowser.Common</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 6f778882a..6b59800d5 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Model.Signed</id> - <version>3.0.638</version> + <version>3.0.639</version> <title>MediaBrowser.Model - Signed Edition</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index b6edd8719..7708f6bbc 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.638</version> + <version>3.0.639</version> <title>Media Browser.Server.Core</title> <authors>Emby Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Emby Server.</description> <copyright>Copyright © Emby 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.638" /> + <dependency id="MediaBrowser.Common" version="3.0.639" /> <dependency id="Interfaces.IO" version="1.0.0.5" /> </dependencies> </metadata> |
