aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj5
-rw-r--r--MediaBrowser.Api/Movies/CollectionService.cs80
-rw-r--r--MediaBrowser.Api/Movies/MoviesService.cs (renamed from MediaBrowser.Api/MoviesService.cs)2
-rw-r--r--MediaBrowser.Api/Movies/TrailersService.cs (renamed from MediaBrowser.Api/TrailersService.cs)2
-rw-r--r--MediaBrowser.Api/SearchService.cs6
-rw-r--r--MediaBrowser.Controller/Collections/CollectionCreationOptions.cs15
-rw-r--r--MediaBrowser.Controller/Collections/ICollectionManager.cs9
-rw-r--r--MediaBrowser.Controller/Entities/BaseItem.cs9
-rw-r--r--MediaBrowser.Controller/Entities/BasePluginFolder.cs15
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs70
-rw-r--r--MediaBrowser.Controller/Entities/LinkedChild.cs10
-rw-r--r--MediaBrowser.Controller/Library/IUserManager.cs3
-rw-r--r--MediaBrowser.Model/ApiClient/IApiClient.cs63
-rw-r--r--MediaBrowser.Model/Querying/ItemSortBy.cs3
-rw-r--r--MediaBrowser.Model/Search/SearchQuery.cs4
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs10
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs129
-rw-r--r--MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/Photos/PhotoProvider.cs1
-rw-r--r--MediaBrowser.Providers/Savers/XmlSaverHelpers.cs39
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionManager.cs130
-rw-r--r--MediaBrowser.Server.Implementations/Collections/CollectionsDynamicFolder.cs55
-rw-r--r--MediaBrowser.Server.Implementations/Library/SearchEngine.cs6
-rw-r--r--MediaBrowser.Server.Implementations/Library/UserManager.cs9
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs3
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj17
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs54
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs46
-rw-r--r--MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs30
-rw-r--r--MediaBrowser.Server.Implementations/packages.config2
-rw-r--r--MediaBrowser.ServerApplication/App.config16
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs5
-rw-r--r--MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj6
-rw-r--r--MediaBrowser.Tests/app.config2
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs1
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj8
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
40 files changed, 798 insertions, 80 deletions
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 @@
<Compile Include="..\SharedVersion.cs">
<Link>Properties\SharedVersion.cs</Link>
</Compile>
+ <Compile Include="Movies\CollectionService.cs" />
<Compile Include="Music\AlbumsService.cs" />
<Compile Include="AppThemeService.cs" />
<Compile Include="BaseApiService.cs" />
@@ -91,7 +92,7 @@
<Compile Include="Library\LibraryStructureService.cs" />
<Compile Include="LiveTv\LiveTvService.cs" />
<Compile Include="LocalizationService.cs" />
- <Compile Include="MoviesService.cs" />
+ <Compile Include="Movies\MoviesService.cs" />
<Compile Include="NewsService.cs" />
<Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" />
@@ -118,7 +119,7 @@
<Compile Include="SessionsService.cs" />
<Compile Include="SimilarItemsHelper.cs" />
<Compile Include="SystemService.cs" />
- <Compile Include="TrailersService.cs" />
+ <Compile Include="Movies\TrailersService.cs" />
<Compile Include="TvShowsService.cs" />
<Compile Include="UserLibrary\ArtistsService.cs" />
<Compile Include="UserLibrary\BaseItemsByNameService.cs" />
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/MoviesService.cs b/MediaBrowser.Api/Movies/MoviesService.cs
index 2a99bca8b..5d97d13e1 100644
--- a/MediaBrowser.Api/MoviesService.cs
+++ b/MediaBrowser.Api/Movies/MoviesService.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using ServiceStack;
-namespace MediaBrowser.Api
+namespace MediaBrowser.Api.Movies
{
/// <summary>
/// Class GetSimilarMovies
diff --git a/MediaBrowser.Api/TrailersService.cs b/MediaBrowser.Api/Movies/TrailersService.cs
index ca465b5e3..4e17bc7b5 100644
--- a/MediaBrowser.Api/TrailersService.cs
+++ b/MediaBrowser.Api/Movies/TrailersService.cs
@@ -5,7 +5,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence;
using ServiceStack;
-namespace MediaBrowser.Api
+namespace MediaBrowser.Api.Movies
{
/// <summary>
/// Class GetSimilarTrailers
diff --git a/MediaBrowser.Api/SearchService.cs b/MediaBrowser.Api/SearchService.cs
index 18bd8c695..455cf6a50 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(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray()
}).ConfigureAwait(false);
diff --git a/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs b/MediaBrowser.Controller/Collections/CollectionCreationOptions.cs
index d26bf5b35..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 Guid? ParentId { get; set; }
public bool IsLocked { get; set; }
+
+ public Dictionary<string, string> ProviderIds { get; set; }
+
+ public CollectionCreationOptions()
+ {
+ ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
+ }
}
}
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.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
- /// <param name="itemId">The item identifier.</param>
+ /// <param name="itemIds">The item ids.</param>
/// <returns>Task.</returns>
- Task AddToCollection(Guid collectionId, Guid itemId);
+ Task AddToCollection(Guid collectionId, IEnumerable<Guid> itemIds);
/// <summary>
/// Removes from collection.
/// </summary>
/// <param name="collectionId">The collection identifier.</param>
- /// <param name="itemId">The item identifier.</param>
+ /// <param name="itemIds">The item ids.</param>
/// <returns>Task.</returns>
- Task RemoveFromCollection(Guid collectionId, Guid itemId);
+ Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> 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
@@ -125,6 +125,15 @@ namespace MediaBrowser.Controller.Entities
}
[IgnoreDataMember]
+ public virtual bool IsHidden
+ {
+ get
+ {
+ return false;
+ }
+ }
+
+ [IgnoreDataMember]
public virtual bool IsOwnedItem
{
get
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
{
/// <summary>
@@ -8,18 +7,6 @@ namespace MediaBrowser.Controller.Entities
/// </summary>
public abstract class BasePluginFolder : Folder, ICollectionFolder, IByReferenceItem
{
- /// <summary>
- /// Gets or sets the type of the location.
- /// </summary>
- /// <value>The type of the location.</value>
- 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..ee371680e 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -264,7 +264,7 @@ namespace MediaBrowser.Controller.Entities
[IgnoreDataMember]
public IEnumerable<BaseItem> Children
{
- get { return ActualChildren; }
+ get { return ActualChildren.Where(i => !i.IsHidden); }
}
/// <summary>
@@ -745,9 +745,9 @@ namespace MediaBrowser.Controller.Entities
var list = new List<BaseItem>();
- AddChildrenToList(user, includeLinkedChildren, list, false, null);
+ var hasLinkedChildren = AddChildrenToList(user, includeLinkedChildren, list, false, null);
- return list;
+ return hasLinkedChildren ? list.DistinctBy(i => i.Id).ToList() : list;
}
/// <summary>
@@ -905,13 +905,6 @@ namespace MediaBrowser.Controller.Entities
/// <returns>BaseItem.</returns>
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<bool> RefreshedOwnedItems(MetadataRefreshOptions options, List<FileSystemInfo> 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<BaseItem> GetHiddenChildren()
+ {
+ return ActualChildren.Where(i => i.IsHidden);
+ }
}
}
diff --git a/MediaBrowser.Controller/Entities/LinkedChild.cs b/MediaBrowser.Controller/Entities/LinkedChild.cs
index cc5f7bf38..1ae04e40f 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; }
+
/// <summary>
/// Serves as a cache
/// </summary>
@@ -18,8 +22,8 @@ namespace MediaBrowser.Controller.Entities
public enum LinkedChildType
{
- Manual = 1,
- Shortcut = 2
+ Manual = 0,
+ Shortcut = 1
}
public class LinkedChildComparer : IEqualityComparer<LinkedChild>
@@ -35,7 +39,7 @@ namespace MediaBrowser.Controller.Entities
public int GetHashCode(LinkedChild obj)
{
- return (obj.Path + obj.Type.ToString()).GetHashCode();
+ return (obj.Path + obj.Type).GetHashCode();
}
}
}
diff --git a/MediaBrowser.Controller/Library/IUserManager.cs b/MediaBrowser.Controller/Library/IUserManager.cs
index 0502ec419..c3b0748cf 100644
--- a/MediaBrowser.Controller/Library/IUserManager.cs
+++ b/MediaBrowser.Controller/Library/IUserManager.cs
@@ -51,9 +51,8 @@ namespace MediaBrowser.Controller.Library
/// Refreshes metadata for each user
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
/// <returns>Task.</returns>
- Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false);
+ Task RefreshUsersMetadata(CancellationToken cancellationToken);
/// <summary>
/// Renames the user.
diff --git a/MediaBrowser.Model/ApiClient/IApiClient.cs b/MediaBrowser.Model/ApiClient/IApiClient.cs
index 2a965114b..a0e527a99 100644
--- a/MediaBrowser.Model/ApiClient/IApiClient.cs
+++ b/MediaBrowser.Model/ApiClient/IApiClient.cs
@@ -1017,12 +1017,53 @@ namespace MediaBrowser.Model.ApiClient
Task<QueryResult<ProgramInfoDto>> GetLiveTvProgramsAsync(ProgramQuery query, CancellationToken cancellationToken);
/// <summary>
+ /// Gets the live tv program asynchronous.
+ /// </summary>
+ /// <param name="id">The identifier.</param>
+ /// <param name="userId">The user identifier.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{ProgramInfoDto}.</returns>
+ Task<ProgramInfoDto> GetLiveTvProgramAsync(string id, string userId, CancellationToken cancellationToken);
+
+ /// <summary>
/// Gets the recommended live tv programs asynchronous.
/// </summary>
/// <param name="query">The query.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{QueryResult{ProgramInfoDto}}.</returns>
Task<QueryResult<ProgramInfoDto>> GetRecommendedLiveTvProgramsAsync(RecommendedProgramQuery query, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Creates the live tv timer asynchronous.
+ /// </summary>
+ /// <param name="timer">The timer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task CreateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Updates the live tv timer asynchronous.
+ /// </summary>
+ /// <param name="timer">The timer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task UpdateLiveTvTimerAsync(TimerInfoDto timer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Creates the live tv series timer asynchronous.
+ /// </summary>
+ /// <param name="timer">The timer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task CreateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Updates the live tv series timer asynchronous.
+ /// </summary>
+ /// <param name="timer">The timer.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task.</returns>
+ Task UpdateLiveTvSeriesTimerAsync(SeriesTimerInfoDto timer, CancellationToken cancellationToken);
/// <summary>
/// Gets the live tv timer asynchronous.
@@ -1071,5 +1112,27 @@ namespace MediaBrowser.Model.ApiClient
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
Task DeleteLiveTvRecordingAsync(string id, CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the default timer information.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{SeriesTimerInfoDto}.</returns>
+ Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the live tv guide information.
+ /// </summary>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{GuideInfo}.</returns>
+ Task<GuideInfo> GetLiveTvGuideInfo(CancellationToken cancellationToken);
+
+ /// <summary>
+ /// Gets the default timer information.
+ /// </summary>
+ /// <param name="programId">The program identifier.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>Task{SeriesTimerInfoDto}.</returns>
+ Task<SeriesTimerInfoDto> GetDefaultLiveTvTimerInfo(string programId, CancellationToken cancellationToken);
}
} \ No newline at end of file
diff --git a/MediaBrowser.Model/Querying/ItemSortBy.cs b/MediaBrowser.Model/Querying/ItemSortBy.cs
index 09b8f0e18..b07db396a 100644
--- a/MediaBrowser.Model/Querying/ItemSortBy.cs
+++ b/MediaBrowser.Model/Querying/ItemSortBy.cs
@@ -86,5 +86,8 @@ namespace MediaBrowser.Model.Querying
public const string VideoBitRate = "VideoBitRate";
public const string AirTime = "AirTime";
public const string Metascore = "Metascore";
+ public const string Studio = "Studio";
+ public const string Players = "Players";
+ public const string GameSystem = "GameSystem";
}
}
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/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
index 9547eedd9..49e616d1a 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs
@@ -1,5 +1,6 @@
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
@@ -37,6 +38,15 @@ namespace MediaBrowser.Providers.BoxSets
protected override void MergeData(BoxSet source, BoxSet target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
{
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+
+ if (mergeMetadataSettings)
+ {
+ var list = source.LinkedChildren.ToList();
+
+ list.AddRange(target.LinkedChildren.Where(i => i.Type == LinkedChildType.Shortcut));
+
+ target.LinkedChildren = list;
+ }
}
protected override ItemUpdateType BeforeSave(BoxSet item)
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs b/MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs
new file mode 100644
index 000000000..eb3c99cef
--- /dev/null
+++ b/MediaBrowser.Providers/BoxSets/BoxSetXmlParser.cs
@@ -0,0 +1,129 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Movies;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Xml;
+
+namespace MediaBrowser.Providers.BoxSets
+{
+ public class BoxSetXmlParser : BaseItemXmlParser<BoxSet>
+ {
+ private readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+ public BoxSetXmlParser(ILogger logger)
+ : base(logger)
+ {
+ }
+
+ protected override void FetchDataFromXmlNode(XmlReader reader, BoxSet item)
+ {
+ switch (reader.Name)
+ {
+ case "CollectionItems":
+
+ using (var subReader = reader.ReadSubtree())
+ {
+ FetchFromCollectionItemsNode(subReader, item);
+ }
+ break;
+
+ default:
+ base.FetchDataFromXmlNode(reader, item);
+ break;
+ }
+ }
+
+ private void FetchFromCollectionItemsNode(XmlReader reader, BoxSet item)
+ {
+ reader.MoveToContent();
+
+ var list = new List<LinkedChild>();
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "CollectionItem":
+ {
+ using (var subReader = reader.ReadSubtree())
+ {
+ var child = GetLinkedChild(subReader);
+
+ if (child != null)
+ {
+ list.Add(child);
+ }
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ item.LinkedChildren = list;
+ }
+
+ private LinkedChild GetLinkedChild(XmlReader reader)
+ {
+ reader.MoveToContent();
+
+ var linkedItem = new LinkedChild
+ {
+ Type = LinkedChildType.Manual
+ };
+
+ while (reader.Read())
+ {
+ if (reader.NodeType == XmlNodeType.Element)
+ {
+ switch (reader.Name)
+ {
+ case "Name":
+ {
+ linkedItem.ItemName = reader.ReadElementContentAsString();
+ break;
+ }
+
+ case "Type":
+ {
+ linkedItem.ItemType = reader.ReadElementContentAsString();
+ break;
+ }
+
+ case "Year":
+ {
+ var val = reader.ReadElementContentAsString();
+
+ if (!string.IsNullOrWhiteSpace(val))
+ {
+ int rval;
+
+ if (int.TryParse(val, NumberStyles.Integer, UsCulture, out rval))
+ {
+ linkedItem.ItemYear = rval;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ reader.Skip();
+ break;
+ }
+ }
+ }
+
+ return string.IsNullOrWhiteSpace(linkedItem.ItemName) || string.IsNullOrWhiteSpace(linkedItem.ItemType) ? null : linkedItem;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
index e9896c28e..77ea52fa9 100644
--- a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
+++ b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs
@@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.BoxSets
protected override void Fetch(LocalMetadataResult<BoxSet> result, string path, CancellationToken cancellationToken)
{
- new BaseItemXmlParser<BoxSet>(_logger).Fetch(result.Item, path, cancellationToken);
+ new BoxSetXmlParser(_logger).Fetch(result.Item, path, cancellationToken);
}
protected override FileInfo GetXmlFile(ItemInfo info, IDirectoryService directoryService)
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index a5ea1b64b..79cbdfa68 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -75,6 +75,7 @@
<Compile Include="All\LocalImageProvider.cs" />
<Compile Include="Books\BookMetadataService.cs" />
<Compile Include="BoxSets\BoxSetMetadataService.cs" />
+ <Compile Include="BoxSets\BoxSetXmlParser.cs" />
<Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
<Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
<Compile Include="Folders\CollectionFolderImageProvider.cs" />
diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs
index 66344d1d1..a89919d3f 100644
--- a/MediaBrowser.Providers/Photos/PhotoProvider.cs
+++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs
@@ -25,6 +25,7 @@ namespace MediaBrowser.Providers.Photos
public Task<ItemUpdateType> FetchAsync(Photo item, IDirectoryService directoryService, CancellationToken cancellationToken)
{
item.SetImagePath(ImageType.Primary, item.Path);
+ item.SetImagePath(ImageType.Backdrop, item.Path);
if (item.Path.EndsWith(".jpg", StringComparison.OrdinalIgnoreCase) || item.Path.EndsWith(".jpeg", StringComparison.OrdinalIgnoreCase))
{
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("</Persons>");
}
+
+ 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("<CollectionItems>");
+ foreach (var link in items)
+ {
+ builder.Append("<CollectionItem>");
+
+ builder.Append("<Name>" + SecurityElement.Escape(link.ItemName) + "</Name>");
+ builder.Append("<Type>" + SecurityElement.Escape(link.ItemType) + "</Type>");
+
+ if (link.ItemYear.HasValue)
+ {
+ builder.Append("<Year>" + SecurityElement.Escape(link.ItemYear.Value.ToString(UsCulture)) + "</Year>");
+ }
+
+ builder.Append("</CollectionItem>");
+ }
+ builder.Append("</CollectionItems>");
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs b/MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
index da444d100..1cfcef514 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;
@@ -28,9 +30,12 @@ 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 = _libraryManager.GetItemById(options.ParentId) as Folder;
+ var parentFolder = GetParentFolder(options.ParentId);
if (parentFolder == null)
{
@@ -51,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);
@@ -66,14 +72,124 @@ 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<ManualCollectionsFolder>().FirstOrDefault() ??
+ _libraryManager.RootFolder.GetHiddenChildren().OfType<ManualCollectionsFolder>().FirstOrDefault();
+ }
+
+ public async Task AddToCollection(Guid collectionId, IEnumerable<Guid> ids)
+ {
+ var collection = _libraryManager.GetItemById(collectionId) as BoxSet;
+
+ if (collection == null)
+ {
+ throw new ArgumentException("No collection exists with the supplied Id");
+ }
+
+ var list = new List<LinkedChild>();
+
+ 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 Task RemoveFromCollection(Guid collectionId, Guid itemId)
+ public async Task RemoveFromCollection(Guid collectionId, IEnumerable<Guid> itemIds)
{
- 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<LinkedChild>();
+
+ 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);
+ }
+
+ var shortcutFiles = Directory
+ .EnumerateFiles(collection.Path, "*", SearchOption.TopDirectoryOnly)
+ .Where(i => _fileSystem.IsShortcut(i))
+ .ToList();
+
+ var shortcutFilesToDelete = list.Where(child => !string.IsNullOrWhiteSpace(child.Path) && child.Type == LinkedChildType.Shortcut)
+ .Select(child => shortcutFiles.FirstOrDefault(i => string.Equals(child.Path, _fileSystem.ResolveShortcut(i), StringComparison.OrdinalIgnoreCase)))
+ .Where(i => !string.IsNullOrWhiteSpace(i))
+ .ToList();
+
+ foreach (var file in shortcutFilesToDelete)
+ {
+ _iLibraryMonitor.ReportFileSystemChangeBeginning(file);
+ }
+
+ try
+ {
+ foreach (var file in shortcutFilesToDelete)
+ {
+ File.Delete(file);
+ }
+
+ foreach (var child in list)
+ {
+ collection.LinkedChildren.Remove(child);
+ }
+ }
+ finally
+ {
+ foreach (var file in shortcutFilesToDelete)
+ {
+ _iLibraryMonitor.ReportFileSystemChangeComplete(file, false);
+ }
+ }
+
+ 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/Library/UserManager.cs b/MediaBrowser.Server.Implementations/Library/UserManager.cs
index 8654a26ce..06028d37e 100644
--- a/MediaBrowser.Server.Implementations/Library/UserManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/UserManager.cs
@@ -189,15 +189,10 @@ namespace MediaBrowser.Server.Implementations.Library
/// Refreshes metadata for each user
/// </summary>
/// <param name="cancellationToken">The cancellation token.</param>
- /// <param name="force">if set to <c>true</c> [force].</param>
/// <returns>Task.</returns>
- public Task RefreshUsersMetadata(CancellationToken cancellationToken, bool force = false)
+ public Task RefreshUsersMetadata(CancellationToken cancellationToken)
{
- var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions
- {
- ReplaceAllMetadata = force
-
- }, cancellationToken)).ToList();
+ var tasks = Users.Select(user => user.RefreshMetadata(new MetadataRefreshOptions(), cancellationToken)).ToList();
return Task.WhenAll(tasks);
}
diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
index bd315530e..7c0361a1f 100644
--- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
+++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
@@ -582,7 +582,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
.Select(i => _libraryManager.GetGenre(i))
.ToDictionary(i => i.Name, StringComparer.OrdinalIgnoreCase);
- programs = programList.OrderByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres))
+ programs = programList.OrderBy(i => i.HasImage(ImageType.Primary) ? 0 : 1)
+ .ThenByDescending(i => GetRecommendationScore(i, user.Id, serviceName, genres))
.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..78902f0dc 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -65,12 +65,9 @@
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
- <Reference Include="System.Data.SQLite, Version=1.0.90.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=x86" Condition=" '$(ConfigurationName)' != 'Release Mono' ">
+ <Reference Include="System.Data.SQLite, Version=1.0.91.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
- <HintPath>..\packages\System.Data.SQLite.x86.1.0.90.0\lib\net45\System.Data.SQLite.dll</HintPath>
- </Reference>
- <Reference Include="System.Data.SQLite.Linq" Condition=" '$(ConfigurationName)' != 'Release Mono' ">
- <HintPath>..\packages\System.Data.SQLite.x86.1.0.90.0\lib\net45\System.Data.SQLite.Linq.dll</HintPath>
+ <HintPath>..\packages\System.Data.SQLite.Core.1.0.91.3\lib\net45\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="Microsoft.CSharp" />
@@ -110,6 +107,7 @@
</Compile>
<Compile Include="BdInfo\BdInfoExaminer.cs" />
<Compile Include="Collections\CollectionManager.cs" />
+ <Compile Include="Collections\CollectionsDynamicFolder.cs" />
<Compile Include="Configuration\ServerConfigurationManager.cs" />
<Compile Include="Drawing\ImageHeader.cs" />
<Compile Include="Drawing\PercentPlayedDrawer.cs" />
@@ -227,6 +225,7 @@
<Compile Include="Sorting\DateCreatedComparer.cs" />
<Compile Include="Sorting\DatePlayedComparer.cs" />
<Compile Include="Sorting\EpisodeCountComparer.cs" />
+ <Compile Include="Sorting\GameSystemComparer.cs" />
<Compile Include="Sorting\IsFolderComparer.cs" />
<Compile Include="Sorting\IsUnplayedComparer.cs" />
<Compile Include="Sorting\MetascoreComparer.cs" />
@@ -235,6 +234,7 @@
<Compile Include="Sorting\NameComparer.cs" />
<Compile Include="Sorting\OfficialRatingComparer.cs" />
<Compile Include="Sorting\PlayCountComparer.cs" />
+ <Compile Include="Sorting\PlayersComparer.cs" />
<Compile Include="Sorting\PremiereDateComparer.cs" />
<Compile Include="Sorting\ProductionYearComparer.cs" />
<Compile Include="Sorting\RandomComparer.cs" />
@@ -248,6 +248,7 @@
<Compile Include="Persistence\SqliteItemRepository.cs" />
<Compile Include="Persistence\SqliteUserDataRepository.cs" />
<Compile Include="Persistence\SqliteUserRepository.cs" />
+ <Compile Include="Sorting\StudioComparer.cs" />
<Compile Include="Sorting\TrailerCountComparer.cs" />
<Compile Include="Sorting\VideoBitRateComparer.cs" />
<Compile Include="Themes\AppThemeManager.cs" />
@@ -378,6 +379,12 @@
<Link>swagger-ui\swagger-ui.min.js</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="x64\SQLite.Interop.dll" Condition=" '$(ConfigurationName)' != 'Release Mono' ">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
+ <Content Include="x86\SQLite.Interop.dll" Condition=" '$(ConfigurationName)' != 'Release Mono' ">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ </Content>
<EmbeddedResource Include="Localization\Ratings\be.txt" />
</ItemGroup>
<ItemGroup>
diff --git a/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs b/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs
new file mode 100644
index 000000000..eb83b98e9
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sorting/GameSystemComparer.cs
@@ -0,0 +1,54 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+using System;
+
+namespace MediaBrowser.Server.Implementations.Sorting
+{
+ public class GameSystemComparer : IBaseItemComparer
+ {
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem x, BaseItem y)
+ {
+ return string.Compare(GetValue(x), GetValue(y), StringComparison.CurrentCultureIgnoreCase);
+ }
+
+ /// <summary>
+ /// Gets the value.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <returns>System.String.</returns>
+ private string GetValue(BaseItem x)
+ {
+ var game = x as Game;
+
+ if (game != null)
+ {
+ return game.GameSystem;
+ }
+
+ var system = x as GameSystem;
+
+ if (system != null)
+ {
+ return system.GameSystemName;
+ }
+
+ return string.Empty;
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get { return ItemSortBy.GameSystem; }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs b/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs
new file mode 100644
index 000000000..5bcd080d7
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sorting/PlayersComparer.cs
@@ -0,0 +1,46 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+
+namespace MediaBrowser.Server.Implementations.Sorting
+{
+ public class PlayersComparer : IBaseItemComparer
+ {
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem x, BaseItem y)
+ {
+ return GetValue(x).CompareTo(GetValue(y));
+ }
+
+ /// <summary>
+ /// Gets the value.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <returns>System.String.</returns>
+ private int GetValue(BaseItem x)
+ {
+ var game = x as Game;
+
+ if (game != null)
+ {
+ return game.PlayersSupported ?? 0;
+ }
+
+ return 0;
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get { return ItemSortBy.Players; }
+ }
+ }
+}
diff --git a/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs b/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs
new file mode 100644
index 000000000..83ab4dfc2
--- /dev/null
+++ b/MediaBrowser.Server.Implementations/Sorting/StudioComparer.cs
@@ -0,0 +1,30 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Sorting;
+using MediaBrowser.Model.Querying;
+using System.Linq;
+
+namespace MediaBrowser.Server.Implementations.Sorting
+{
+ public class StudioComparer : IBaseItemComparer
+ {
+ /// <summary>
+ /// Compares the specified x.
+ /// </summary>
+ /// <param name="x">The x.</param>
+ /// <param name="y">The y.</param>
+ /// <returns>System.Int32.</returns>
+ public int Compare(BaseItem x, BaseItem y)
+ {
+ return AlphanumComparator.CompareValues(x.Studios.FirstOrDefault() ?? string.Empty, y.Studios.FirstOrDefault() ?? string.Empty);
+ }
+
+ /// <summary>
+ /// Gets the name.
+ /// </summary>
+ /// <value>The name.</value>
+ public string Name
+ {
+ get { return ItemSortBy.Studio; }
+ }
+ }
+}
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 @@
<package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" />
<package id="Mono.Nat" version="1.2.3" targetFramework="net45" />
<package id="morelinq" version="1.0.16006" targetFramework="net45" />
- <package id="System.Data.SQLite.x86" version="1.0.90.0" targetFramework="net45" />
+ <package id="System.Data.SQLite.Core" version="1.0.91.3" targetFramework="net45" />
</packages> \ 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 @@
<configuration>
<configSections>
<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog" />
+
+ <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</configSections>
<system.diagnostics>
<assert assertuienabled="false" />
@@ -43,7 +45,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-1.0.89.0" newVersion="1.0.89.0" />
+ <bindingRedirect oldVersion="0.0.0.0-1.0.91.0" newVersion="1.0.91.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="SimpleInjector" publicKeyToken="984cb50dea722e99" culture="neutral" />
@@ -63,4 +65,16 @@
</providers>
</roleManager>
</system.web>
+ <entityFramework>
+ <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
+ <parameters>
+ <parameter value="v11.0" />
+ </parameters>
+ </defaultConnectionFactory>
+ <providers>
+ <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
+ <provider invariantName="System.Data.SQLite.EF6" type="System.Data.SQLite.EF6.SQLiteProviderServices, System.Data.SQLite.EF6" />
+ </providers>
+ </entityFramework>
+
</configuration> \ 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<IAppThemeManager>(appThemeManager);
+ var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);
+ RegisterSingleInstance<ICollectionManager>(collectionManager);
+
LiveTvManager = new LiveTvManager(ServerConfigurationManager, FileSystemManager, Logger, ItemRepository, ImageProcessor, UserDataManager, DtoService, UserManager, LibraryManager, TaskManager);
RegisterSingleInstance(LiveTvManager);
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index c3eb4fd6a..227d0dd1d 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -235,6 +235,12 @@ xcopy "$(TargetDir)*.dll" "$(SolutionDir)..\Deploy\Server\System" /y
mkdir "$(SolutionDir)..\Deploy\Server\System\dashboard-ui"
xcopy "$(TargetDir)dashboard-ui" "$(SolutionDir)..\Deploy\Server\System\dashboard-ui" /y /s
+mkdir "$(SolutionDir)..\Deploy\Server\System\x86"
+xcopy "$(TargetDir)x86" "$(SolutionDir)..\Deploy\Server\System\x86" /y /s
+
+mkdir "$(SolutionDir)..\Deploy\Server\System\x64"
+xcopy "$(TargetDir)x64" "$(SolutionDir)..\Deploy\Server\System\x64" /y /s
+
del "$(SolutionDir)..\Deploy\MBServer.zip"
"$(SolutionDir)ThirdParty\7zip\7za" a -mx9 "$(SolutionDir)..\Deploy\MBServer.zip" "$(SolutionDir)..\Deploy\Server\*"
)</PostBuildEvent>
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 @@
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Data.SQLite" publicKeyToken="db937bc2d44ff139" culture="neutral" />
- <bindingRedirect oldVersion="0.0.0.0-1.0.89.0" newVersion="1.0.89.0" />
+ <bindingRedirect oldVersion="0.0.0.0-1.0.91.0" newVersion="1.0.91.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
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 @@
<Content Include="dashboard-ui\dashboardinfopage.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\editcollectionitems.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\encodingsettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -261,7 +264,7 @@
<Content Include="dashboard-ui\allusersettings.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\boxsets.html">
+ <Content Include="dashboard-ui\collections.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\clients\mbkinect.png">
@@ -480,6 +483,9 @@
<Content Include="dashboard-ui\scripts\dashboardinfo.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\editcollectionitems.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\encodingsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec
index 7bd20e7d4..3e548e625 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.336</version>
+ <version>3.0.339</version>
<title>MediaBrowser.Common.Internal</title>
<authors>Luke</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.336" />
+ <dependency id="MediaBrowser.Common" version="3.0.339" />
<dependency id="NLog" version="2.1.0" />
<dependency id="SimpleInjector" version="2.4.1" />
<dependency id="sharpcompress" version="0.10.2" />
diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec
index f68a42741..5b81a046e 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.336</version>
+ <version>3.0.339</version>
<title>MediaBrowser.Common</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec
index 97f4c90a0..bf8bfd157 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.336</version>
+ <version>3.0.339</version>
<title>Media Browser.Server.Core</title>
<authors>Media Browser Team</authors>
<owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
<description>Contains core components required to build plugins for Media Browser Server.</description>
<copyright>Copyright © Media Browser 2013</copyright>
<dependencies>
- <dependency id="MediaBrowser.Common" version="3.0.336" />
+ <dependency id="MediaBrowser.Common" version="3.0.339" />
</dependencies>
</metadata>
<files>