support grouping behind boxsets

This commit is contained in:
Luke Pulverenti 2014-03-15 00:14:07 -04:00
parent 02bfc112ce
commit d55af4f529
13 changed files with 269 additions and 34 deletions

View File

@ -238,6 +238,9 @@ namespace MediaBrowser.Api.UserLibrary
[ApiMember(Name = "HasOfficialRating", Description = "Optional filter by items that have official ratings", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public bool? HasOfficialRating { get; set; }
[ApiMember(Name = "CollapseBoxSetItems", Description = "Whether or not to hide items behind their boxsets.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "GET")]
public bool CollapseBoxSetItems { get; set; }
}
/// <summary>
@ -315,6 +318,11 @@ namespace MediaBrowser.Api.UserLibrary
items = items.AsEnumerable();
if (request.CollapseBoxSetItems)
{
items = CollapseItemsWithinBoxSets(items, user);
}
items = ApplySortOrder(request, items, user, _libraryManager);
// This must be the last filter
@ -1218,6 +1226,41 @@ namespace MediaBrowser.Api.UserLibrary
return false;
}
private IEnumerable<BaseItem> CollapseItemsWithinBoxSets(IEnumerable<BaseItem> items, User user)
{
var itemsToCollapse = new List<ISupportsBoxSetGrouping>();
var boxsets = new List<BaseItem>();
var list = items.ToList();
foreach (var item in list.OfType<ISupportsBoxSetGrouping>())
{
var currentBoxSets = item.BoxSetIdList
.Select(i => _libraryManager.GetItemById(i))
.Where(i => i != null && i.IsVisible(user))
.ToList();
if (currentBoxSets.Count > 0)
{
itemsToCollapse.Add(item);
boxsets.AddRange(currentBoxSets);
}
}
return list.Except(itemsToCollapse.Cast<BaseItem>()).Concat(boxsets).Distinct();
}
private bool AllowBoxSetCollapsing(GetItems request)
{
// Only allow when using default sort order
if (!string.IsNullOrEmpty(request.SortBy) && !string.Equals(request.SortBy, "SortName", StringComparison.OrdinalIgnoreCase))
{
return false;
}
return true;
}
internal static IEnumerable<BaseItem> FilterForAdjacency(IEnumerable<BaseItem> items, string adjacentToId)
{
var list = items.ToList();

View File

@ -1,7 +1,7 @@

namespace MediaBrowser.Controller.Dlna
{
public class DlnaProfile
public class DeviceProfile
{
/// <summary>
/// Gets or sets the name.
@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.Dlna
/// <value>The direct play profiles.</value>
public DirectPlayProfile[] DirectPlayProfiles { get; set; }
public DlnaProfile()
public DeviceProfile()
{
DirectPlayProfiles = new DirectPlayProfile[] { };
TranscodingProfiles = new TranscodingProfile[] { };

View File

@ -1,25 +1,97 @@

using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Xml.Serialization;
namespace MediaBrowser.Controller.Dlna
{
public class DirectPlayProfile
{
public string[] Containers { get; set; }
public string[] AudioCodecs { get; set; }
public string[] VideoCodecs { get; set; }
public string Container { get; set; }
public string AudioCodec { get; set; }
public string VideoCodec { get; set; }
[IgnoreDataMember]
[XmlIgnore]
public string[] Containers
{
get
{
return (Container ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
set
{
Container = value == null ? null : string.Join(",", value);
}
}
[IgnoreDataMember]
[XmlIgnore]
public string[] AudioCodecs
{
get
{
return (AudioCodec ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
set
{
AudioCodec = value == null ? null : string.Join(",", value);
}
}
[IgnoreDataMember]
[XmlIgnore]
public string[] VideoCodecs
{
get
{
return (VideoCodec ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
}
set
{
VideoCodec = value == null ? null : string.Join(",", value);
}
}
public string MimeType { get; set; }
public DlnaProfileType Type { get; set; }
public List<ProfileCondition> Conditions { get; set; }
public DirectPlayProfile()
{
Containers = new string[] { };
AudioCodecs = new string[] { };
VideoCodecs = new string[] { };
Conditions = new List<ProfileCondition>();
}
}
public class ProfileCondition
{
public ProfileConditionType Condition { get; set; }
public ProfileConditionValue Value { get; set; }
}
public enum DlnaProfileType
{
Audio = 0,
Video = 1
}
public enum ProfileConditionType
{
Equals = 0,
NotEquals = 1,
LessThanEqual = 2,
GreaterThanEqual = 3
}
public enum ProfileConditionValue
{
AudioChannels,
AudioBitrate,
Filesize,
VideoWidth,
VideoHeight,
VideoBitrate,
VideoFramerate
}
}

View File

@ -8,13 +8,13 @@ namespace MediaBrowser.Controller.Dlna
/// Gets the dlna profiles.
/// </summary>
/// <returns>IEnumerable{DlnaProfile}.</returns>
IEnumerable<DlnaProfile> GetProfiles();
IEnumerable<DeviceProfile> GetProfiles();
/// <summary>
/// Gets the default profile.
/// </summary>
/// <returns>DlnaProfile.</returns>
DlnaProfile GetDefaultProfile();
DeviceProfile GetDefaultProfile();
/// <summary>
/// Gets the profile.
@ -23,6 +23,6 @@ namespace MediaBrowser.Controller.Dlna
/// <param name="modelName">Name of the model.</param>
/// <param name="modelNumber">The model number.</param>
/// <returns>DlnaProfile.</returns>
DlnaProfile GetProfile(string friendlyName, string modelName, string modelNumber);
DeviceProfile GetProfile(string friendlyName, string modelName, string modelNumber);
}
}

View File

@ -0,0 +1,19 @@
using System;
using System.Collections.Generic;
namespace MediaBrowser.Controller.Entities
{
/// <summary>
/// Marker interface to denote a class that supports being hidden underneath it's boxset.
/// Just about anything can be placed into a boxset,
/// but movies should also only appear underneath and not outside separately (subject to configuration).
/// </summary>
public interface ISupportsBoxSetGrouping
{
/// <summary>
/// Gets or sets the box set identifier list.
/// </summary>
/// <value>The box set identifier list.</value>
List<Guid> BoxSetIdList { get; set; }
}
}

View File

@ -1,11 +1,11 @@
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Configuration;
using MediaBrowser.Model.Entities;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading;
using System.Threading.Tasks;
@ -14,7 +14,7 @@ namespace MediaBrowser.Controller.Entities.Movies
/// <summary>
/// Class Movie
/// </summary>
public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>
public class Movie : Video, IHasCriticRating, IHasSoundtracks, IHasBudget, IHasKeywords, IHasTrailers, IHasThemeMedia, IHasTaglines, IHasPreferredMetadataLanguage, IHasAwards, IHasMetascore, IHasLookupInfo<MovieInfo>, ISupportsBoxSetGrouping
{
public List<Guid> SpecialFeatureIds { get; set; }
@ -23,6 +23,12 @@ namespace MediaBrowser.Controller.Entities.Movies
public List<Guid> ThemeSongIds { get; set; }
public List<Guid> ThemeVideoIds { get; set; }
/// <summary>
/// This is just a cache to enable quick access by Id
/// </summary>
[IgnoreDataMember]
public List<Guid> BoxSetIdList { get; set; }
/// <summary>
/// Gets or sets the preferred metadata country code.
/// </summary>
@ -39,6 +45,7 @@ namespace MediaBrowser.Controller.Entities.Movies
LocalTrailerIds = new List<Guid>();
ThemeSongIds = new List<Guid>();
ThemeVideoIds = new List<Guid>();
BoxSetIdList = new List<Guid>();
Taglines = new List<string>();
Keywords = new List<string>();
}

View File

@ -75,7 +75,7 @@
<Compile Include="Collections\ICollectionManager.cs" />
<Compile Include="Dlna\DirectPlayProfile.cs" />
<Compile Include="Dlna\IDlnaManager.cs" />
<Compile Include="Dlna\DlnaProfile.cs" />
<Compile Include="Dlna\DeviceProfile.cs" />
<Compile Include="Dlna\TranscodingProfile.cs" />
<Compile Include="Drawing\IImageProcessor.cs" />
<Compile Include="Drawing\ImageFormat.cs" />
@ -114,6 +114,7 @@
<Compile Include="Entities\ILibraryItem.cs" />
<Compile Include="Entities\ImageSourceInfo.cs" />
<Compile Include="Entities\IMetadataContainer.cs" />
<Compile Include="Entities\ISupportsBoxSetGrouping.cs" />
<Compile Include="Entities\ISupportsPlaceHolders.cs" />
<Compile Include="Entities\ItemImageInfo.cs" />
<Compile Include="Entities\LinkedChild.cs" />

View File

@ -1,4 +1,7 @@
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.IO;
using MediaBrowser.Controller.Dlna;
using MediaBrowser.Model.Serialization;
using System.Collections.Generic;
using System.Text.RegularExpressions;
@ -6,11 +9,23 @@ namespace MediaBrowser.Dlna
{
public class DlnaManager : IDlnaManager
{
public IEnumerable<DlnaProfile> GetProfiles()
{
var list = new List<DlnaProfile>();
private IApplicationPaths _appPaths;
private readonly IXmlSerializer _xmlSerializer;
private readonly IFileSystem _fileSystem;
list.Add(new DlnaProfile
public DlnaManager(IXmlSerializer xmlSerializer, IFileSystem fileSystem)
{
_xmlSerializer = xmlSerializer;
_fileSystem = fileSystem;
//GetProfiles();
}
public IEnumerable<DeviceProfile> GetProfiles()
{
var list = new List<DeviceProfile>();
list.Add(new DeviceProfile
{
Name = "Samsung TV (B Series)",
ClientType = "DLNA",
@ -59,7 +74,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "Samsung TV (E/F-series)",
ClientType = "DLNA",
@ -107,7 +122,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "Samsung TV (C/D-series)",
ClientType = "DLNA",
@ -154,7 +169,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "Xbox 360",
ClientType = "DLNA",
@ -189,7 +204,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "Xbox One",
ModelName = "Xbox One",
@ -225,7 +240,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "Sony Bravia (2012)",
ClientType = "DLNA",
@ -262,7 +277,7 @@ namespace MediaBrowser.Dlna
});
//WDTV does not need any transcoding of the formats we support statically
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
Name = "WDTV Live",
ClientType = "DLNA",
@ -284,7 +299,7 @@ namespace MediaBrowser.Dlna
}
});
list.Add(new DlnaProfile
list.Add(new DeviceProfile
{
//Linksys DMA2100us does not need any transcoding of the formats we support statically
Name = "Linksys DMA2100",
@ -307,12 +322,17 @@ namespace MediaBrowser.Dlna
}
});
foreach (var item in list)
{
//_xmlSerializer.SerializeToFile(item, "d:\\" + _fileSystem.GetValidFilename(item.Name));
}
return list;
}
public DlnaProfile GetDefaultProfile()
public DeviceProfile GetDefaultProfile()
{
return new DlnaProfile
return new DeviceProfile
{
TranscodingProfiles = new[]
{
@ -345,7 +365,7 @@ namespace MediaBrowser.Dlna
};
}
public DlnaProfile GetProfile(string friendlyName, string modelName, string modelNumber)
public DeviceProfile GetProfile(string friendlyName, string modelName, string modelNumber)
{
foreach (var profile in GetProfiles())
{

View File

@ -31,7 +31,7 @@ namespace MediaBrowser.Dlna.PlayTo
public long StartPositionTicks { get; set; }
public static PlaylistItem Create(BaseItem item, DlnaProfile profile)
public static PlaylistItem Create(BaseItem item, DeviceProfile profile)
{
var playlistItem = new PlaylistItem
{
@ -92,7 +92,7 @@ namespace MediaBrowser.Dlna.PlayTo
return true;
}
private static bool IsSupported(DlnaProfile profile, TranscodingProfile transcodingProfile, string path)
private static bool IsSupported(DeviceProfile profile, TranscodingProfile transcodingProfile, string path)
{
// Placeholder for future conditions
return true;

View File

@ -126,6 +126,18 @@ namespace MediaBrowser.Server.Implementations.Collections
ItemType = item.GetType().Name,
Type = LinkedChildType.Manual
});
var supportsGrouping = item as ISupportsBoxSetGrouping;
if (supportsGrouping != null)
{
var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
if (!boxsetIdList.Contains(collectionId))
{
boxsetIdList.Add(collectionId);
}
supportsGrouping.BoxSetIdList = boxsetIdList;
}
}
collection.LinkedChildren.AddRange(list);
@ -156,6 +168,16 @@ namespace MediaBrowser.Server.Implementations.Collections
}
list.Add(child);
var childItem = _libraryManager.GetItemById(itemId);
var supportsGrouping = childItem as ISupportsBoxSetGrouping;
if (supportsGrouping != null)
{
var boxsetIdList = supportsGrouping.BoxSetIdList.ToList();
boxsetIdList.Remove(collectionId);
supportsGrouping.BoxSetIdList = boxsetIdList;
}
}
var shortcutFiles = Directory

View File

@ -0,0 +1,50 @@
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Library;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace MediaBrowser.Server.Implementations.Library.Validators
{
public class BoxSetPostScanTask : ILibraryPostScanTask
{
private readonly ILibraryManager _libraryManager;
public BoxSetPostScanTask(ILibraryManager libraryManager)
{
_libraryManager = libraryManager;
}
public Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var items = _libraryManager.RootFolder.RecursiveChildren.ToList();
var boxsets = items.OfType<BoxSet>().ToList();
var numComplete = 0;
foreach (var boxset in boxsets)
{
foreach (var child in boxset.GetLinkedChildren().OfType<ISupportsBoxSetGrouping>())
{
var boxsetIdList = child.BoxSetIdList.ToList();
if (!boxsetIdList.Contains(boxset.Id))
{
boxsetIdList.Add(boxset.Id);
}
child.BoxSetIdList = boxsetIdList;
}
numComplete++;
double percent = numComplete;
percent /= boxsets.Count;
progress.Report(percent * 100);
}
progress.Report(100);
return Task.FromResult(true);
}
}
}

View File

@ -165,6 +165,7 @@
<Compile Include="Library\UserManager.cs" />
<Compile Include="Library\Validators\ArtistsPostScanTask.cs" />
<Compile Include="Library\Validators\ArtistsValidator.cs" />
<Compile Include="Library\Validators\BoxSetPostScanTask.cs" />
<Compile Include="Library\Validators\CountHelpers.cs" />
<Compile Include="Library\Validators\GameGenresPostScanTask.cs" />
<Compile Include="Library\Validators\GameGenresValidator.cs" />

View File

@ -492,7 +492,7 @@ namespace MediaBrowser.ServerApplication
var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
RegisterSingleInstance<IAppThemeManager>(appThemeManager);
var dlnaManager = new DlnaManager();
var dlnaManager = new DlnaManager(XmlSerializer, FileSystemManager);
RegisterSingleInstance<IDlnaManager>(dlnaManager);
var collectionManager = new CollectionManager(LibraryManager, FileSystemManager, LibraryMonitor);