|
@@ -1,4 +1,5 @@
|
|
|
using MediaBrowser.Common.Extensions;
|
|
|
+using MediaBrowser.Controller.Channels;
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
using MediaBrowser.Controller.Drawing;
|
|
|
using MediaBrowser.Controller.Entities;
|
|
@@ -9,8 +10,10 @@ using MediaBrowser.Controller.Library;
|
|
|
using MediaBrowser.Dlna.Didl;
|
|
|
using MediaBrowser.Dlna.Server;
|
|
|
using MediaBrowser.Dlna.Service;
|
|
|
+using MediaBrowser.Model.Channels;
|
|
|
using MediaBrowser.Model.Dlna;
|
|
|
using MediaBrowser.Model.Entities;
|
|
|
+using MediaBrowser.Model.Library;
|
|
|
using MediaBrowser.Model.Logging;
|
|
|
using MediaBrowser.Model.Querying;
|
|
|
using System;
|
|
@@ -19,6 +22,7 @@ using System.Globalization;
|
|
|
using System.Linq;
|
|
|
using System.Text;
|
|
|
using System.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
using System.Xml;
|
|
|
|
|
|
namespace MediaBrowser.Dlna.ContentDirectory
|
|
@@ -40,14 +44,18 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
private readonly DidlBuilder _didlBuilder;
|
|
|
|
|
|
private readonly DeviceProfile _profile;
|
|
|
+ private readonly IUserViewManager _userViewManager;
|
|
|
+ private readonly IChannelManager _channelManager;
|
|
|
|
|
|
- public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config)
|
|
|
+ public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, IUserViewManager userViewManager, IChannelManager channelManager)
|
|
|
: base(config, logger)
|
|
|
{
|
|
|
_libraryManager = libraryManager;
|
|
|
_userDataManager = userDataManager;
|
|
|
_user = user;
|
|
|
_systemUpdateId = systemUpdateId;
|
|
|
+ _userViewManager = userViewManager;
|
|
|
+ _channelManager = channelManager;
|
|
|
_profile = profile;
|
|
|
|
|
|
_didlBuilder = new DidlBuilder(profile, user, imageProcessor, serverAddress);
|
|
@@ -69,7 +77,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
return HandleGetSystemUpdateID();
|
|
|
|
|
|
if (string.Equals(methodName, "Browse", StringComparison.OrdinalIgnoreCase))
|
|
|
- return HandleBrowse(methodParams, user, deviceId);
|
|
|
+ return HandleBrowse(methodParams, user, deviceId).Result;
|
|
|
|
|
|
if (string.Equals(methodName, "X_GetFeatureList", StringComparison.OrdinalIgnoreCase))
|
|
|
return HandleXGetFeatureList();
|
|
@@ -78,7 +86,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
return HandleXSetBookmark(methodParams, user);
|
|
|
|
|
|
if (string.Equals(methodName, "Search", StringComparison.OrdinalIgnoreCase))
|
|
|
- return HandleSearch(methodParams, user, deviceId);
|
|
|
+ return HandleSearch(methodParams, user, deviceId).Result;
|
|
|
|
|
|
throw new ResourceNotFoundException("Unexpected control request name: " + methodName);
|
|
|
}
|
|
@@ -141,7 +149,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
return builder.ToString();
|
|
|
}
|
|
|
|
|
|
- private IEnumerable<KeyValuePair<string, string>> HandleBrowse(Headers sparams, User user, string deviceId)
|
|
|
+ private async Task<IEnumerable<KeyValuePair<string, string>>> HandleBrowse(Headers sparams, User user, string deviceId)
|
|
|
{
|
|
|
var id = sparams["ObjectID"];
|
|
|
var flag = sparams["BrowseFlag"];
|
|
@@ -149,16 +157,20 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
|
|
|
|
|
|
var provided = 0;
|
|
|
- var requested = 0;
|
|
|
- var start = 0;
|
|
|
|
|
|
- if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
|
|
|
+ int? requested = 0;
|
|
|
+ int? start = 0;
|
|
|
+
|
|
|
+ int requestedVal;
|
|
|
+ if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
|
|
|
{
|
|
|
- requested = 0;
|
|
|
+ requested = requestedVal;
|
|
|
}
|
|
|
- if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
|
|
|
+
|
|
|
+ int startVal;
|
|
|
+ if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
|
|
|
{
|
|
|
- start = 0;
|
|
|
+ start = startVal;
|
|
|
}
|
|
|
|
|
|
//var root = GetItem(id) as IMediaFolder;
|
|
@@ -173,34 +185,26 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
|
|
|
var folder = (Folder)GetItemFromObjectId(id, user);
|
|
|
|
|
|
- var children = GetChildrenSorted(folder, user, sortCriteria).ToList();
|
|
|
+ var childrenResult = (await GetChildrenSorted(folder, user, sortCriteria, start, requested).ConfigureAwait(false));
|
|
|
|
|
|
- var totalCount = children.Count;
|
|
|
+ var totalCount = childrenResult.TotalRecordCount;
|
|
|
|
|
|
if (string.Equals(flag, "BrowseMetadata"))
|
|
|
{
|
|
|
- result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, children.Count, filter));
|
|
|
+ result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, folder, totalCount, filter));
|
|
|
provided++;
|
|
|
}
|
|
|
else
|
|
|
{
|
|
|
- if (start > 0)
|
|
|
- {
|
|
|
- children = children.Skip(start).ToList();
|
|
|
- }
|
|
|
- if (requested > 0)
|
|
|
- {
|
|
|
- children = children.Take(requested).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- provided = children.Count;
|
|
|
+ provided = childrenResult.Items.Length;
|
|
|
|
|
|
- foreach (var i in children)
|
|
|
+ foreach (var i in childrenResult.Items)
|
|
|
{
|
|
|
if (i.IsFolder)
|
|
|
{
|
|
|
var f = (Folder)i;
|
|
|
- var childCount = GetChildrenSorted(f, user, sortCriteria).Count();
|
|
|
+ var childCount = (await GetChildrenSorted(f, user, sortCriteria, null, 0).ConfigureAwait(false))
|
|
|
+ .TotalRecordCount;
|
|
|
|
|
|
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
|
|
|
}
|
|
@@ -222,7 +226,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- private IEnumerable<KeyValuePair<string, string>> HandleSearch(Headers sparams, User user, string deviceId)
|
|
|
+ private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
|
|
|
{
|
|
|
var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
|
|
|
var sortCriteria = new SortCriteria(sparams.GetValueOrDefault("SortCriteria", ""));
|
|
@@ -230,16 +234,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
|
|
|
// sort example: dc:title, dc:date
|
|
|
|
|
|
- var requested = 0;
|
|
|
- var start = 0;
|
|
|
+ int? requested = 0;
|
|
|
+ int? start = 0;
|
|
|
|
|
|
- if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requested) && requested <= 0)
|
|
|
+ int requestedVal;
|
|
|
+ if (sparams.ContainsKey("RequestedCount") && int.TryParse(sparams["RequestedCount"], out requestedVal) && requestedVal > 0)
|
|
|
{
|
|
|
- requested = 0;
|
|
|
+ requested = requestedVal;
|
|
|
}
|
|
|
- if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out start) && start <= 0)
|
|
|
+
|
|
|
+ int startVal;
|
|
|
+ if (sparams.ContainsKey("StartingIndex") && int.TryParse(sparams["StartingIndex"], out startVal) && startVal > 0)
|
|
|
{
|
|
|
- start = 0;
|
|
|
+ start = startVal;
|
|
|
}
|
|
|
|
|
|
//var root = GetItem(id) as IMediaFolder;
|
|
@@ -259,27 +266,19 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
|
|
|
var folder = (Folder)GetItemFromObjectId(sparams["ContainerID"], user);
|
|
|
|
|
|
- var children = GetChildrenSorted(folder, user, searchCriteria, sortCriteria).ToList();
|
|
|
+ var childrenResult = (await GetChildrenSorted(folder, user, searchCriteria, sortCriteria, start, requested).ConfigureAwait(false));
|
|
|
|
|
|
- var totalCount = children.Count;
|
|
|
+ var totalCount = childrenResult.TotalRecordCount;
|
|
|
|
|
|
- if (start > 0)
|
|
|
- {
|
|
|
- children = children.Skip(start).ToList();
|
|
|
- }
|
|
|
- if (requested > 0)
|
|
|
- {
|
|
|
- children = children.Take(requested).ToList();
|
|
|
- }
|
|
|
-
|
|
|
- var provided = children.Count;
|
|
|
+ var provided = childrenResult.Items.Length;
|
|
|
|
|
|
- foreach (var i in children)
|
|
|
+ foreach (var i in childrenResult.Items)
|
|
|
{
|
|
|
if (i.IsFolder)
|
|
|
{
|
|
|
var f = (Folder)i;
|
|
|
- var childCount = GetChildrenSorted(f, user, searchCriteria, sortCriteria).Count();
|
|
|
+ var childCount = (await GetChildrenSorted(f, user, searchCriteria, sortCriteria, null, 0).ConfigureAwait(false))
|
|
|
+ .TotalRecordCount;
|
|
|
|
|
|
result.DocumentElement.AppendChild(_didlBuilder.GetFolderElement(result, f, childCount, filter));
|
|
|
}
|
|
@@ -300,15 +299,16 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
};
|
|
|
}
|
|
|
|
|
|
- private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort)
|
|
|
+ private async Task<QueryResult<BaseItem>> GetChildrenSorted(Folder folder, User user, SearchCriteria search, SortCriteria sort, int? startIndex, int? limit)
|
|
|
{
|
|
|
if (search.SearchType == SearchType.Unknown)
|
|
|
{
|
|
|
- return GetChildrenSorted(folder, user, sort);
|
|
|
+ return await GetChildrenSorted(folder, user, sort, startIndex, limit).ConfigureAwait(false);
|
|
|
}
|
|
|
|
|
|
- var items = folder.GetRecursiveChildren(user);
|
|
|
- items = FilterUnsupportedContent(items);
|
|
|
+ var result = await GetChildrenSorted(folder, user, sort, null, null).ConfigureAwait(false);
|
|
|
+
|
|
|
+ var items = FilterUnsupportedContent(result.Items);
|
|
|
|
|
|
if (search.SearchType == SearchType.Audio)
|
|
|
{
|
|
@@ -324,12 +324,123 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
}
|
|
|
else if (search.SearchType == SearchType.Playlist)
|
|
|
{
|
|
|
+
|
|
|
}
|
|
|
|
|
|
- return SortItems(items, user, sort);
|
|
|
+ items = SortItems(items, user, sort);
|
|
|
+
|
|
|
+ return ToResult(items, startIndex, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<QueryResult<BaseItem>> GetChildrenSorted(Folder folder, User user, SortCriteria sort, int? startIndex, int? limit)
|
|
|
+ {
|
|
|
+ if (folder is UserRootFolder)
|
|
|
+ {
|
|
|
+ var result = await _userViewManager.GetUserViews(new UserViewQuery
|
|
|
+ {
|
|
|
+ UserId = user.Id.ToString("N")
|
|
|
+
|
|
|
+ }, CancellationToken.None).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return ToResult(result, startIndex, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ var view = folder as UserView;
|
|
|
+
|
|
|
+ if (view != null)
|
|
|
+ {
|
|
|
+ var result = await GetUserViewChildren(view, user, sort).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return ToResult(result, startIndex, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ var channel = folder as Channel;
|
|
|
+
|
|
|
+ if (channel != null)
|
|
|
+ {
|
|
|
+ return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
|
|
|
+ {
|
|
|
+ ChannelId = channel.Id.ToString("N"),
|
|
|
+ Limit = limit,
|
|
|
+ StartIndex = startIndex,
|
|
|
+ UserId = user.Id.ToString("N")
|
|
|
+
|
|
|
+ }, CancellationToken.None);
|
|
|
+ }
|
|
|
+
|
|
|
+ var channelFolderItem = folder as ChannelFolderItem;
|
|
|
+
|
|
|
+ if (channelFolderItem != null)
|
|
|
+ {
|
|
|
+ return await _channelManager.GetChannelItemsInternal(new ChannelItemQuery
|
|
|
+ {
|
|
|
+ ChannelId = channelFolderItem.ChannelId,
|
|
|
+ FolderId = channelFolderItem.Id.ToString("N"),
|
|
|
+ Limit = limit,
|
|
|
+ StartIndex = startIndex,
|
|
|
+ UserId = user.Id.ToString("N")
|
|
|
+
|
|
|
+ }, CancellationToken.None);
|
|
|
+ }
|
|
|
+
|
|
|
+ return ToResult(GetPlainFolderChildrenSorted(folder, user, sort), startIndex, limit);
|
|
|
+ }
|
|
|
+
|
|
|
+ private QueryResult<BaseItem> ToResult(IEnumerable<BaseItem> items, int? startIndex, int? limit)
|
|
|
+ {
|
|
|
+ var list = items.ToArray();
|
|
|
+ var totalCount = list.Length;
|
|
|
+
|
|
|
+ if (startIndex.HasValue)
|
|
|
+ {
|
|
|
+ list = list.Skip(startIndex.Value).ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (limit.HasValue)
|
|
|
+ {
|
|
|
+ list = list.Take(limit.Value).ToArray();
|
|
|
+ }
|
|
|
+
|
|
|
+ return new QueryResult<BaseItem>
|
|
|
+ {
|
|
|
+ Items = list,
|
|
|
+ TotalRecordCount = totalCount
|
|
|
+ };
|
|
|
+ }
|
|
|
+
|
|
|
+ private async Task<IEnumerable<BaseItem>> GetUserViewChildren(UserView folder, User user, SortCriteria sort)
|
|
|
+ {
|
|
|
+ if (string.Equals(folder.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return new List<BaseItem>();
|
|
|
+ }
|
|
|
+ if (string.Equals(folder.ViewType, CollectionType.Channels, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ var result = await _channelManager.GetChannelsInternal(new ChannelQuery()
|
|
|
+ {
|
|
|
+ UserId = user.Id.ToString("N")
|
|
|
+
|
|
|
+ }, CancellationToken.None).ConfigureAwait(false);
|
|
|
+
|
|
|
+ return result.Items;
|
|
|
+ }
|
|
|
+ if (string.Equals(folder.ViewType, CollectionType.TvShows, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return SortItems(folder.GetChildren(user, true).OfType<Series>(), user, sort);
|
|
|
+ }
|
|
|
+ if (string.Equals(folder.ViewType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return GetPlainFolderChildrenSorted(folder, user, sort);
|
|
|
+ }
|
|
|
+ if (string.Equals(folder.ViewType, CollectionType.Music, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ return SortItems(folder.GetChildren(user, true).OfType<MusicArtist>(), user, sort);
|
|
|
+ }
|
|
|
+
|
|
|
+ return GetPlainFolderChildrenSorted(folder, user, sort);
|
|
|
}
|
|
|
|
|
|
- private IEnumerable<BaseItem> GetChildrenSorted(Folder folder, User user, SortCriteria sort)
|
|
|
+ private IEnumerable<BaseItem> GetPlainFolderChildrenSorted(Folder folder, User user, SortCriteria sort)
|
|
|
{
|
|
|
var items = folder.GetChildren(user, true);
|
|
|
|
|
@@ -345,7 +456,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
|
|
|
private IEnumerable<BaseItem> SortItems(IEnumerable<BaseItem> items, User user, SortCriteria sort)
|
|
|
{
|
|
|
- return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending);
|
|
|
+ return _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, sort.SortOrder);
|
|
|
}
|
|
|
|
|
|
private IEnumerable<BaseItem> FilterUnsupportedContent(IEnumerable<BaseItem> items)
|
|
@@ -353,14 +464,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
return items.Where(i =>
|
|
|
{
|
|
|
// Unplayable
|
|
|
- // TODO: Display and prevent playback with restricted flag?
|
|
|
if (i.LocationType == LocationType.Virtual)
|
|
|
{
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// Unplayable
|
|
|
- // TODO: Display and prevent playback with restricted flag?
|
|
|
var supportsPlaceHolder = i as ISupportsPlaceHolders;
|
|
|
if (supportsPlaceHolder != null && supportsPlaceHolder.IsPlaceHolder)
|
|
|
{
|
|
@@ -368,7 +477,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
|
|
|
}
|
|
|
|
|
|
// Upnp renderers won't understand these
|
|
|
- // TODO: Display and prevent playback with restricted flag?
|
|
|
if (i is Game || i is Book)
|
|
|
{
|
|
|
return false;
|