From 2349c8099d04c6c0631cd33e6c74b404381946ab Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 6 Mar 2014 00:17:13 -0500 Subject: start on manual collection creation --- .../Collections/CollectionCreationOptions.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 MediaBrowser.Controller/Collections/CollectionCreationOptions.cs (limited to 'MediaBrowser.Controller/Collections/CollectionCreationOptions.cs') diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs new file mode 100644 index 000000000..d26bf5b35 --- /dev/null +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -0,0 +1,13 @@ +using System; + +namespace MediaBrowser.Controller.Collections +{ + public class CollectionCreationOptions + { + public string Name { get; set; } + + public Guid ParentId { get; set; } + + public bool IsLocked { get; set; } + } +} -- cgit v1.2.3 From e00985d07ca3923f7f558b8592c0d092842bff5d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 7 Mar 2014 10:53:23 -0500 Subject: #715 - Support creating/editing collections (boxsets) in web client --- MediaBrowser.Api/MediaBrowser.Api.csproj | 5 +- MediaBrowser.Api/Movies/CollectionService.cs | 80 +++++++++++++++++++ MediaBrowser.Api/Movies/MoviesService.cs | 82 +++++++++++++++++++ MediaBrowser.Api/Movies/TrailersService.cs | 75 ++++++++++++++++++ MediaBrowser.Api/MoviesService.cs | 82 ------------------- MediaBrowser.Api/SearchService.cs | 6 +- MediaBrowser.Api/TrailersService.cs | 75 ------------------ .../Collections/CollectionCreationOptions.cs | 2 +- .../Collections/ICollectionManager.cs | 9 ++- MediaBrowser.Controller/Entities/BaseItem.cs | 9 +++ .../Entities/BasePluginFolder.cs | 15 +--- MediaBrowser.Controller/Entities/Folder.cs | 66 ++++++++++++---- MediaBrowser.Controller/Entities/LinkedChild.cs | 4 + MediaBrowser.Model/Search/SearchQuery.cs | 4 + MediaBrowser.Providers/Savers/XmlSaverHelpers.cs | 39 ++++++++- .../Collections/CollectionManager.cs | 92 ++++++++++++++++++++-- .../Collections/CollectionsDynamicFolder.cs | 55 +++++++++++++ .../Library/SearchEngine.cs | 6 ++ .../LiveTv/LiveTvManager.cs | 1 + .../MediaBrowser.Server.Implementations.csproj | 14 ++-- .../packages.config | 2 +- MediaBrowser.ServerApplication/App.config | 16 +++- MediaBrowser.ServerApplication/ApplicationHost.cs | 5 ++ MediaBrowser.Tests/app.config | 2 +- MediaBrowser.WebDashboard/Api/DashboardService.cs | 1 + .../MediaBrowser.WebDashboard.csproj | 8 +- 26 files changed, 545 insertions(+), 210 deletions(-) create mode 100644 MediaBrowser.Api/Movies/CollectionService.cs create mode 100644 MediaBrowser.Api/Movies/MoviesService.cs create mode 100644 MediaBrowser.Api/Movies/TrailersService.cs delete mode 100644 MediaBrowser.Api/MoviesService.cs delete mode 100644 MediaBrowser.Api/TrailersService.cs create mode 100644 MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs (limited to 'MediaBrowser.Controller/Collections/CollectionCreationOptions.cs') diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 85e40eda1..e19fbb967 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -66,6 +66,7 @@ Properties\SharedVersion.cs + @@ -91,7 +92,7 @@ - + @@ -118,7 +119,7 @@ - + diff --git a/MediaBrowser.Api/Movies/CollectionService.cs b/MediaBrowser.Api/Movies/CollectionService.cs new file mode 100644 index 000000000..456449b7b --- /dev/null +++ b/MediaBrowser.Api/Movies/CollectionService.cs @@ -0,0 +1,80 @@ +using MediaBrowser.Controller.Collections; +using ServiceStack; +using System; +using System.Linq; +using System.Threading.Tasks; + +namespace MediaBrowser.Api.Movies +{ + [Route("/Collections", "POST")] + [Api(Description = "Creates a new collection")] + public class CreateCollection : IReturnVoid + { + [ApiMember(Name = "IsLocked", Description = "Whether or not to lock the new collection.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] + public bool IsLocked { get; set; } + + [ApiMember(Name = "Name", Description = "The name of the new collection.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Name { get; set; } + + [ApiMember(Name = "ParentId", Description = "Optional - create the collection within a specific folder", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "POST")] + public Guid? ParentId { get; set; } + } + + [Route("/Collections/{Id}/Items", "POST")] + [Api(Description = "Adds items to a collection")] + public class AddToCollection : IReturnVoid + { + [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Ids { get; set; } + + [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")] + public Guid Id { get; set; } + } + + [Route("/Collections/{Id}/Items", "DELETE")] + [Api(Description = "Removes items from a collection")] + public class RemoveFromCollection : IReturnVoid + { + [ApiMember(Name = "Ids", Description = "Item id, comma delimited", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "POST")] + public string Ids { get; set; } + + [ApiMember(Name = "Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")] + public Guid Id { get; set; } + } + + public class CollectionService : BaseApiService + { + private readonly ICollectionManager _collectionManager; + + public CollectionService(ICollectionManager collectionManager) + { + _collectionManager = collectionManager; + } + + public void Post(CreateCollection request) + { + var task = _collectionManager.CreateCollection(new CollectionCreationOptions + { + IsLocked = request.IsLocked, + Name = request.Name, + ParentId = request.ParentId + }); + + Task.WaitAll(task); + } + + public void Post(AddToCollection request) + { + var task = _collectionManager.AddToCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i))); + + Task.WaitAll(task); + } + + public void Delete(RemoveFromCollection request) + { + var task = _collectionManager.RemoveFromCollection(request.Id, request.Ids.Split(',').Select(i => new Guid(i))); + + Task.WaitAll(task); + } + } +} diff --git a/MediaBrowser.Api/Movies/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs new file mode 100644 index 000000000..5d97d13e1 --- /dev/null +++ b/MediaBrowser.Api/Movies/MoviesService.cs @@ -0,0 +1,82 @@ +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; +using ServiceStack; + +namespace MediaBrowser.Api.Movies +{ + /// + /// Class GetSimilarMovies + /// + [Route("/Movies/{Id}/Similar", "GET")] + [Api(Description = "Finds movies and trailers similar to a given movie.")] + public class GetSimilarMovies : BaseGetSimilarItemsFromItem + { + [ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] + public bool IncludeTrailers { get; set; } + + public GetSimilarMovies() + { + IncludeTrailers = true; + } + } + + /// + /// Class MoviesService + /// + public class MoviesService : BaseApiService + { + /// + /// The _user manager + /// + private readonly IUserManager _userManager; + + /// + /// The _user data repository + /// + private readonly IUserDataManager _userDataRepository; + /// + /// The _library manager + /// + private readonly ILibraryManager _libraryManager; + + private readonly IItemRepository _itemRepo; + private readonly IDtoService _dtoService; + + /// + /// Initializes a new instance of the class. + /// + /// The user manager. + /// The user data repository. + /// The library manager. + public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + { + _userManager = userManager; + _userDataRepository = userDataRepository; + _libraryManager = libraryManager; + _itemRepo = itemRepo; + _dtoService = dtoService; + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetSimilarMovies request) + { + var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, + _itemRepo, + _libraryManager, + _userDataRepository, + _dtoService, + Logger, + request, item => item is Movie || (item is Trailer && request.IncludeTrailers), + SimilarItemsHelper.GetSimiliarityScore); + + return ToOptimizedSerializedResultUsingCache(result); + } + } +} diff --git a/MediaBrowser.Api/Movies/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs new file mode 100644 index 000000000..4e17bc7b5 --- /dev/null +++ b/MediaBrowser.Api/Movies/TrailersService.cs @@ -0,0 +1,75 @@ +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Persistence; +using ServiceStack; + +namespace MediaBrowser.Api.Movies +{ + /// + /// Class GetSimilarTrailers + /// + [Route("/Trailers/{Id}/Similar", "GET")] + [Api(Description = "Finds movies and trailers similar to a given trailer.")] + public class GetSimilarTrailers : BaseGetSimilarItemsFromItem + { + } + + /// + /// Class TrailersService + /// + public class TrailersService : BaseApiService + { + /// + /// The _user manager + /// + private readonly IUserManager _userManager; + + /// + /// The _user data repository + /// + private readonly IUserDataManager _userDataRepository; + /// + /// The _library manager + /// + private readonly ILibraryManager _libraryManager; + + private readonly IItemRepository _itemRepo; + private readonly IDtoService _dtoService; + + /// + /// Initializes a new instance of the class. + /// + /// The user manager. + /// The user data repository. + /// The library manager. + public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) + { + _userManager = userManager; + _userDataRepository = userDataRepository; + _libraryManager = libraryManager; + _itemRepo = itemRepo; + _dtoService = dtoService; + } + + /// + /// Gets the specified request. + /// + /// The request. + /// System.Object. + public object Get(GetSimilarTrailers request) + { + var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, + _itemRepo, + _libraryManager, + _userDataRepository, + _dtoService, + Logger, + request, item => item is Movie || item is Trailer, + SimilarItemsHelper.GetSimiliarityScore); + + return ToOptimizedSerializedResultUsingCache(result); + } + } +} diff --git a/MediaBrowser.Api/MoviesService.cs b/MediaBrowser.Api/MoviesService.cs deleted file mode 100644 index 2a99bca8b..000000000 --- a/MediaBrowser.Api/MoviesService.cs +++ /dev/null @@ -1,82 +0,0 @@ -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using ServiceStack; - -namespace MediaBrowser.Api -{ - /// - /// Class GetSimilarMovies - /// - [Route("/Movies/{Id}/Similar", "GET")] - [Api(Description = "Finds movies and trailers similar to a given movie.")] - public class GetSimilarMovies : BaseGetSimilarItemsFromItem - { - [ApiMember(Name = "IncludeTrailers", Description = "Whether or not to include trailers within the results. Defaults to true.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] - public bool IncludeTrailers { get; set; } - - public GetSimilarMovies() - { - IncludeTrailers = true; - } - } - - /// - /// Class MoviesService - /// - public class MoviesService : BaseApiService - { - /// - /// The _user manager - /// - private readonly IUserManager _userManager; - - /// - /// The _user data repository - /// - private readonly IUserDataManager _userDataRepository; - /// - /// The _library manager - /// - private readonly ILibraryManager _libraryManager; - - private readonly IItemRepository _itemRepo; - private readonly IDtoService _dtoService; - - /// - /// Initializes a new instance of the class. - /// - /// The user manager. - /// The user data repository. - /// The library manager. - public MoviesService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) - { - _userManager = userManager; - _userDataRepository = userDataRepository; - _libraryManager = libraryManager; - _itemRepo = itemRepo; - _dtoService = dtoService; - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetSimilarMovies request) - { - var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, - _itemRepo, - _libraryManager, - _userDataRepository, - _dtoService, - Logger, - request, item => item is Movie || (item is Trailer && request.IncludeTrailers), - SimilarItemsHelper.GetSimiliarityScore); - - return ToOptimizedSerializedResultUsingCache(result); - } - } -} diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs index 18bd8c695..c3da87d40 100644 --- a/MediaBrowser.Api/SearchService.cs +++ b/MediaBrowser.Api/SearchService.cs @@ -63,6 +63,9 @@ namespace MediaBrowser.Api [ApiMember(Name = "IncludeArtists", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")] public bool IncludeArtists { get; set; } + [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string IncludeItemTypes { get; set; } + public GetSearchHints() { IncludeArtists = true; @@ -130,7 +133,8 @@ namespace MediaBrowser.Api IncludePeople = request.IncludePeople, IncludeStudios = request.IncludeStudios, StartIndex = request.StartIndex, - UserId = request.UserId + UserId = request.UserId, + IncludeItemTypes = (request.IncludeItemTypes ?? string.Empty).Split(',') }).ConfigureAwait(false); diff --git a/MediaBrowser.Api/TrailersService.cs b/MediaBrowser.Api/TrailersService.cs deleted file mode 100644 index ca465b5e3..000000000 --- a/MediaBrowser.Api/TrailersService.cs +++ /dev/null @@ -1,75 +0,0 @@ -using MediaBrowser.Controller.Dto; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Persistence; -using ServiceStack; - -namespace MediaBrowser.Api -{ - /// - /// Class GetSimilarTrailers - /// - [Route("/Trailers/{Id}/Similar", "GET")] - [Api(Description = "Finds movies and trailers similar to a given trailer.")] - public class GetSimilarTrailers : BaseGetSimilarItemsFromItem - { - } - - /// - /// Class TrailersService - /// - public class TrailersService : BaseApiService - { - /// - /// The _user manager - /// - private readonly IUserManager _userManager; - - /// - /// The _user data repository - /// - private readonly IUserDataManager _userDataRepository; - /// - /// The _library manager - /// - private readonly ILibraryManager _libraryManager; - - private readonly IItemRepository _itemRepo; - private readonly IDtoService _dtoService; - - /// - /// Initializes a new instance of the class. - /// - /// The user manager. - /// The user data repository. - /// The library manager. - public TrailersService(IUserManager userManager, IUserDataManager userDataRepository, ILibraryManager libraryManager, IItemRepository itemRepo, IDtoService dtoService) - { - _userManager = userManager; - _userDataRepository = userDataRepository; - _libraryManager = libraryManager; - _itemRepo = itemRepo; - _dtoService = dtoService; - } - - /// - /// Gets the specified request. - /// - /// The request. - /// System.Object. - public object Get(GetSimilarTrailers request) - { - var result = SimilarItemsHelper.GetSimilarItemsResult(_userManager, - _itemRepo, - _libraryManager, - _userDataRepository, - _dtoService, - Logger, - request, item => item is Movie || item is Trailer, - SimilarItemsHelper.GetSimiliarityScore); - - return ToOptimizedSerializedResultUsingCache(result); - } - } -} diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index d26bf5b35..089f9b6ad 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -6,7 +6,7 @@ namespace MediaBrowser.Controller.Collections { public string Name { get; set; } - public Guid ParentId { get; set; } + public Guid? ParentId { get; set; } public bool IsLocked { get; set; } } diff --git a/MediaBrowser.Controller/Collections/ICollectionManager.cs b/MediaBrowser.Controller/Collections/ICollectionManager.cs index a1e6b2c12..d7bc178ad 100644 --- a/MediaBrowser.Controller/Collections/ICollectionManager.cs +++ b/MediaBrowser.Controller/Collections/ICollectionManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Threading.Tasks; namespace MediaBrowser.Controller.Collections @@ -16,16 +17,16 @@ namespace MediaBrowser.Controller.Collections /// Adds to collection. /// /// The collection identifier. - /// The item identifier. + /// The item ids. /// Task. - Task AddToCollection(Guid collectionId, Guid itemId); + Task AddToCollection(Guid collectionId, IEnumerable itemIds); /// /// Removes from collection. /// /// The collection identifier. - /// The item identifier. + /// The item ids. /// Task. - Task RemoveFromCollection(Guid collectionId, Guid itemId); + Task RemoveFromCollection(Guid collectionId, IEnumerable itemIds); } } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 923673bd8..0deebeb32 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -124,6 +124,15 @@ namespace MediaBrowser.Controller.Entities } } + [IgnoreDataMember] + public virtual bool IsHidden + { + get + { + return false; + } + } + [IgnoreDataMember] public virtual bool IsOwnedItem { diff --git a/MediaBrowser.Controller/Entities/BasePluginFolder.cs b/MediaBrowser.Controller/Entities/BasePluginFolder.cs index 8f7071000..29d66718c 100644 --- a/MediaBrowser.Controller/Entities/BasePluginFolder.cs +++ b/MediaBrowser.Controller/Entities/BasePluginFolder.cs @@ -1,5 +1,4 @@ -using MediaBrowser.Model.Entities; - + namespace MediaBrowser.Controller.Entities { /// @@ -8,18 +7,6 @@ namespace MediaBrowser.Controller.Entities /// public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem { - /// - /// Gets or sets the type of the location. - /// - /// The type of the location. - public override LocationType LocationType - { - get - { - return LocationType.Virtual; - } - } - protected BasePluginFolder() { DisplayMediaType = "CollectionFolder"; diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 627f657ab..7dfe7f22e 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities [IgnoreDataMember] public IEnumerable Children { - get { return ActualChildren; } + get { return ActualChildren.Where(i => !i.IsHidden); } } /// @@ -905,13 +905,6 @@ namespace MediaBrowser.Controller.Entities /// BaseItem. private BaseItem GetLinkedChild(LinkedChild info) { - if (string.IsNullOrEmpty(info.Path)) - { - throw new ArgumentException("Encountered linked child with empty path."); - } - - BaseItem item = null; - // First get using the cached Id if (info.ItemId.HasValue) { @@ -920,20 +913,19 @@ namespace MediaBrowser.Controller.Entities return null; } - item = LibraryManager.GetItemById(info.ItemId.Value); - } + var itemById = LibraryManager.GetItemById(info.ItemId.Value); - // If still null, search by path - if (item == null) - { - item = LibraryManager.RootFolder.FindByPath(info.Path); + if (itemById != null) + { + return itemById; + } } + var item = FindLinkedChild(info); + // If still null, log if (item == null) { - Logger.Warn("Unable to find linked item at {0}", info.Path); - // Don't keep searching over and over info.ItemId = Guid.Empty; } @@ -946,6 +938,43 @@ namespace MediaBrowser.Controller.Entities return item; } + private BaseItem FindLinkedChild(LinkedChild info) + { + if (!string.IsNullOrEmpty(info.Path)) + { + var itemByPath = LibraryManager.RootFolder.FindByPath(info.Path); + + if (itemByPath == null) + { + Logger.Warn("Unable to find linked item at path {0}", info.Path); + } + + return itemByPath; + } + + if (!string.IsNullOrWhiteSpace(info.ItemName) && !string.IsNullOrWhiteSpace(info.ItemType)) + { + return LibraryManager.RootFolder.RecursiveChildren.FirstOrDefault(i => + { + if (string.Equals(i.Name, info.ItemName, StringComparison.OrdinalIgnoreCase)) + { + if (string.Equals(i.GetType().Name, info.ItemType, StringComparison.OrdinalIgnoreCase)) + { + if (info.ItemYear.HasValue) + { + return info.ItemYear.Value == (i.ProductionYear ?? -1); + } + return true; + } + } + + return false; + }); + } + + return null; + } + protected override async Task RefreshedOwnedItems(MetadataRefreshOptions options, List fileSystemChildren, CancellationToken cancellationToken) { var changesFound = false; @@ -1106,5 +1135,10 @@ namespace MediaBrowser.Controller.Entities return GetRecursiveChildren(user).Where(i => !i.IsFolder && i.LocationType != LocationType.Virtual) .All(i => i.IsUnplayed(user)); } + + public IEnumerable GetHiddenChildren() + { + return ActualChildren.Where(i => i.IsHidden); + } } } diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs index cc5f7bf38..84af0500d 100644 --- a/MediaBrowser.Controller/Entities/LinkedChild.cs +++ b/MediaBrowser.Controller/Entities/LinkedChild.cs @@ -9,6 +9,10 @@ namespace MediaBrowser.Controller.Entities public string Path { get; set; } public LinkedChildType Type { get; set; } + public string ItemName { get; set; } + public string ItemType { get; set; } + public int? ItemYear { get; set; } + /// /// Serves as a cache /// diff --git a/MediaBrowser.Model/Search/SearchQuery.cs b/MediaBrowser.Model/Search/SearchQuery.cs index 87ff7af66..678dfd39d 100644 --- a/MediaBrowser.Model/Search/SearchQuery.cs +++ b/MediaBrowser.Model/Search/SearchQuery.cs @@ -33,6 +33,8 @@ namespace MediaBrowser.Model.Search public bool IncludeStudios { get; set; } public bool IncludeArtists { get; set; } + public string[] IncludeItemTypes { get; set; } + public SearchQuery() { IncludeArtists = true; @@ -40,6 +42,8 @@ namespace MediaBrowser.Model.Search IncludeMedia = true; IncludePeople = true; IncludeStudios = true; + + IncludeItemTypes = new string[] { }; } } } diff --git a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs index b8ab55db0..03fe5c802 100644 --- a/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs +++ b/MediaBrowser.Providers/Savers/XmlSaverHelpers.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.Entities; @@ -82,7 +83,8 @@ namespace MediaBrowser.Providers.Savers "TVRageId", "VoteCount", "Website", - "Zap2ItId" + "Zap2ItId", + "CollectionItems" }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); @@ -580,6 +582,12 @@ namespace MediaBrowser.Providers.Savers builder.Append(""); } + + var folder = item as BoxSet; + if (folder != null) + { + AddCollectionItems(folder, builder); + } } public static void AddChapters(Video item, StringBuilder builder, IItemRepository repository) @@ -631,5 +639,34 @@ namespace MediaBrowser.Providers.Savers } } } + + public static void AddCollectionItems(Folder item, StringBuilder builder) + { + var items = item.LinkedChildren + .Where(i => i.Type == LinkedChildType.Manual && !string.IsNullOrWhiteSpace(i.ItemName)) + .ToList(); + + if (items.Count == 0) + { + return; + } + + builder.Append(""); + foreach (var link in items) + { + builder.Append(""); + + builder.Append("" + SecurityElement.Escape(link.ItemName) + ""); + builder.Append("" + SecurityElement.Escape(link.ItemType) + ""); + + if (link.ItemYear.HasValue) + { + builder.Append("" + SecurityElement.Escape(link.ItemYear.Value.ToString(UsCulture)) + ""); + } + + builder.Append(""); + } + builder.Append(""); + } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index da444d100..679b629a8 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -5,7 +5,9 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -30,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Collections var folderName = _fileSystem.GetValidFilename(name); - var parentFolder = _libraryManager.GetItemById(options.ParentId) as Folder; + var parentFolder = GetParentFolder(options.ParentId); if (parentFolder == null) { @@ -66,14 +68,94 @@ namespace MediaBrowser.Server.Implementations.Collections } } - public Task AddToCollection(Guid collectionId, Guid itemId) + private Folder GetParentFolder(Guid? parentId) { - throw new NotImplementedException(); + if (parentId.HasValue) + { + if (parentId.Value == Guid.Empty) + { + throw new ArgumentNullException("parentId"); + } + + return _libraryManager.GetItemById(parentId.Value) as Folder; + } + + return _libraryManager.RootFolder.Children.OfType().FirstOrDefault() ?? + _libraryManager.RootFolder.GetHiddenChildren().OfType().FirstOrDefault(); } - public Task RemoveFromCollection(Guid collectionId, Guid itemId) + public async Task AddToCollection(Guid collectionId, IEnumerable ids) { - throw new NotImplementedException(); + var collection = _libraryManager.GetItemById(collectionId) as BoxSet; + + if (collection == null) + { + throw new ArgumentException("No collection exists with the supplied Id"); + } + + var list = new List(); + + foreach (var itemId in ids) + { + var item = _libraryManager.GetItemById(itemId); + + if (item == null) + { + throw new ArgumentException("No item exists with the supplied Id"); + } + + if (collection.LinkedChildren.Any(i => i.ItemId.HasValue && i.ItemId == itemId)) + { + throw new ArgumentException("Item already exists in collection"); + } + + list.Add(new LinkedChild + { + ItemName = item.Name, + ItemYear = item.ProductionYear, + ItemType = item.GetType().Name, + Type = LinkedChildType.Manual + }); + } + + collection.LinkedChildren.AddRange(list); + + await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); + } + + public async Task RemoveFromCollection(Guid collectionId, IEnumerable itemIds) + { + var collection = _libraryManager.GetItemById(collectionId) as BoxSet; + + if (collection == null) + { + throw new ArgumentException("No collection exists with the supplied Id"); + } + + var list = new List(); + + foreach (var itemId in itemIds) + { + var child = collection.LinkedChildren.FirstOrDefault(i => i.ItemId.HasValue && i.ItemId.Value == itemId); + + if (child == null) + { + throw new ArgumentException("No collection title exists with the supplied Id"); + } + + list.Add(child); + } + + foreach (var child in list) + { + collection.LinkedChildren.Remove(child); + } + + await collection.UpdateToRepository(ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false); + + await collection.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs new file mode 100644 index 000000000..834fbcd31 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs @@ -0,0 +1,55 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Controller.Entities; +using System.IO; +using System.Linq; + +namespace MediaBrowser.Server.Implementations.Collections +{ + public class CollectionsDynamicFolder : IVirtualFolderCreator + { + private readonly IApplicationPaths _appPaths; + + public CollectionsDynamicFolder(IApplicationPaths appPaths) + { + _appPaths = appPaths; + } + + public BasePluginFolder GetFolder() + { + var path = Path.Combine(_appPaths.DataPath, "collections"); + + Directory.CreateDirectory(path); + + return new ManualCollectionsFolder + { + Path = path + }; + } + } + + public class ManualCollectionsFolder : BasePluginFolder + { + public ManualCollectionsFolder() + { + Name = "Collections"; + } + + public override bool IsVisible(User user) + { + if (!GetChildren(user, true).Any()) + { + return false; + } + + return base.IsVisible(user); + } + + public override bool IsHidden + { + get + { + return !ActualChildren.Any() || base.IsHidden; + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs index 12686f542..b22fd343b 100644 --- a/MediaBrowser.Server.Implementations/Library/SearchEngine.cs +++ b/MediaBrowser.Server.Implementations/Library/SearchEngine.cs @@ -37,6 +37,12 @@ namespace MediaBrowser.Server.Implementations.Library var results = await GetSearchHints(inputItems, query).ConfigureAwait(false); + // Include item types + if (query.IncludeItemTypes.Length > 0) + { + results = results.Where(f => query.IncludeItemTypes.Contains(f.Item.GetType().Name, StringComparer.OrdinalIgnoreCase)); + } + var searchResultArray = results.ToArray(); results = searchResultArray; diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index bd315530e..104ebfab8 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -583,6 +583,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv .ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase); programs = programList.OrderByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres)) + .ThenBy(i => i.HasImage(ImageType.Primary) ? 0 : 1) .ThenBy(i => i.StartDate); if (query.Limit.HasValue) diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 0165cefad..a0df2c23a 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -65,12 +65,9 @@ - + False - ..\packages\System.Data.SQLite.x86.1.0.90.0\lib\net45\System.Data.SQLite.dll - - - ..\packages\System.Data.SQLite.x86.1.0.90.0\lib\net45\System.Data.SQLite.Linq.dll + ..\packages\System.Data.SQLite.Core.1.0.91.3\lib\net45\System.Data.SQLite.dll @@ -110,6 +107,7 @@ + @@ -378,6 +376,12 @@ swagger-ui\swagger-ui.min.js PreserveNewest + + Always + + + Always + diff --git a/MediaBrowser.Server.Implementations/packages.config b/MediaBrowser.Server.Implementations/packages.config index 3c984e265..f04536190 100644 --- a/MediaBrowser.Server.Implementations/packages.config +++ b/MediaBrowser.Server.Implementations/packages.config @@ -4,5 +4,5 @@ - + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/App.config b/MediaBrowser.ServerApplication/App.config index 53788e09a..978f31851 100644 --- a/MediaBrowser.ServerApplication/App.config +++ b/MediaBrowser.ServerApplication/App.config @@ -2,6 +2,8 @@
+ +
@@ -43,7 +45,7 @@ - + @@ -63,4 +65,16 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 479e07ee6..a3a878537 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -9,6 +9,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; +using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Drawing; using MediaBrowser.Controller.Dto; @@ -36,6 +37,7 @@ using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.BdInfo; +using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Configuration; using MediaBrowser.Server.Implementations.Drawing; using MediaBrowser.Server.Implementations.Dto; @@ -488,6 +490,9 @@ namespace MediaBrowser.ServerApplication var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); RegisterSingleInstance(appThemeManager); + var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor); + RegisterSingleInstance(collectionManager); + LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager); RegisterSingleInstance(LiveTvManager); diff --git a/MediaBrowser.Tests/app.config b/MediaBrowser.Tests/app.config index cbc4501c5..3359125c3 100644 --- a/MediaBrowser.Tests/app.config +++ b/MediaBrowser.Tests/app.config @@ -4,7 +4,7 @@ - + diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index c10b17d67..19f213b2f 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -480,6 +480,7 @@ namespace MediaBrowser.WebDashboard.Api "dashboardinfo.js", "dashboardpage.js", "directorybrowser.js", + "editcollectionitems.js", "edititemmetadata.js", "edititempeople.js", "edititemimages.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 4895d203f..424192e28 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -213,6 +213,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -261,7 +264,7 @@ PreserveNewest - + PreserveNewest @@ -480,6 +483,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest -- cgit v1.2.3 From c6bd890cb2976c4cbf5c278a5987320dcb631652 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 7 Mar 2014 23:20:31 -0500 Subject: refined collection editing --- .../Collections/CollectionCreationOptions.cs | 13 +++++++++++-- .../Collections/CollectionManager.cs | 8 ++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'MediaBrowser.Controller/Collections/CollectionCreationOptions.cs') diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs index 089f9b6ad..e147e0905 100644 --- a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs +++ b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs @@ -1,13 +1,22 @@ -using System; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; namespace MediaBrowser.Controller.Collections { - public class CollectionCreationOptions + public class CollectionCreationOptions : IHasProviderIds { public string Name { get; set; } public Guid? ParentId { get; set; } public bool IsLocked { get; set; } + + public Dictionary ProviderIds { get; set; } + + public CollectionCreationOptions() + { + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); + } } } diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs index 5a5dfdd3e..1cfcef514 100644 --- a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs +++ b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs @@ -30,7 +30,10 @@ namespace MediaBrowser.Server.Implementations.Collections { var name = options.Name; - var folderName = _fileSystem.GetValidFilename(name); + // Need to use the [boxset] suffix + // If internet metadata is not found, or if xml saving is off there will be no collection.xml + // This could cause it to get re-resolved as a plain folder + var folderName = _fileSystem.GetValidFilename(name) + " [boxset]"; var parentFolder = GetParentFolder(options.ParentId); @@ -53,7 +56,8 @@ namespace MediaBrowser.Server.Implementations.Collections Parent = parentFolder, DisplayMediaType = "Collection", Path = path, - DontFetchMeta = options.IsLocked + DontFetchMeta = options.IsLocked, + ProviderIds = options.ProviderIds }; await parentFolder.AddChild(collection, CancellationToken.None).ConfigureAwait(false); -- cgit v1.2.3