diff options
Diffstat (limited to 'Emby.Server.Implementations')
9 files changed, 157 insertions, 172 deletions
diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 3e2dbeefd..80a5defd6 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2622,6 +2622,11 @@ namespace Emby.Server.Implementations.Data groups.Add("PresentationUniqueKey"); } + if (query.GroupBySeriesPresentationUniqueKey) + { + groups.Add("SeriesPresentationUniqueKey"); + } + if (groups.Count > 0) { return " Group by " + string.Join(",", groups.ToArray()); @@ -2934,6 +2939,10 @@ namespace Emby.Server.Implementations.Data { commandText += " select count (distinct PresentationUniqueKey)" + GetFromText(); } + else if (query.GroupBySeriesPresentationUniqueKey) + { + commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText(); + } else { commandText += " select count (guid)" + GetFromText(); @@ -3079,6 +3088,11 @@ namespace Emby.Server.Implementations.Data } if (string.Equals(name, ItemSortBy.DatePlayed, StringComparison.OrdinalIgnoreCase)) { + if (query.GroupBySeriesPresentationUniqueKey) + { + return new Tuple<string, bool>("MAX(LastPlayedDate)", false); + } + return new Tuple<string, bool>("LastPlayedDate", false); } if (string.Equals(name, ItemSortBy.PlayCount, StringComparison.OrdinalIgnoreCase)) @@ -3353,6 +3367,10 @@ namespace Emby.Server.Implementations.Data { commandText += " select count (distinct PresentationUniqueKey)" + GetFromText(); } + else if (query.GroupBySeriesPresentationUniqueKey) + { + commandText += " select count (distinct SeriesPresentationUniqueKey)" + GetFromText(); + } else { commandText += " select count (guid)" + GetFromText(); @@ -4640,6 +4658,11 @@ namespace Emby.Server.Implementations.Data return false; } + if (query.GroupBySeriesPresentationUniqueKey) + { + return false; + } + if (!string.IsNullOrWhiteSpace(query.PresentationUniqueKey)) { return false; diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 396bd8e88..7bd8fe2bf 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -6,19 +6,16 @@ using System; using System.Collections.Generic; using System.Globalization; using System.IO; -using System.IO.Compression; using System.Net; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; using System.Xml; -using Emby.Server.Implementations.HttpServer; using Emby.Server.Implementations.Services; using MediaBrowser.Model.IO; using MediaBrowser.Model.Services; using IRequest = MediaBrowser.Model.Services.IRequest; using MimeTypes = MediaBrowser.Model.Net.MimeTypes; -using StreamWriter = Emby.Server.Implementations.HttpServer.StreamWriter; namespace Emby.Server.Implementations.HttpServer { @@ -193,50 +190,37 @@ namespace Emby.Server.Implementations.HttpServer /// <returns></returns> public object ToOptimizedResult<T>(IRequest request, T dto) { - var compressionType = GetCompressionType(request); - if (compressionType == null) - { - var contentType = request.ResponseContentType; - - switch (GetRealContentType(contentType)) - { - case "application/xml": - case "text/xml": - case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml - return SerializeToXmlString(dto); - - case "application/json": - case "text/json": - return _jsonSerializer.SerializeToString(dto); - } - } + var contentType = request.ResponseContentType; - // Do not use the memoryStreamFactory here, they don't place nice with compression - using (var ms = new MemoryStream()) + switch (GetRealContentType(contentType)) { - var contentType = request.ResponseContentType; - var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); + case "application/xml": + case "text/xml": + case "text/xml; charset=utf-8": //"text/xml; charset=utf-8" also matches xml + return SerializeToXmlString(dto); - writerFn(dto, ms); + case "application/json": + case "text/json": + return _jsonSerializer.SerializeToString(dto); + default: + { + var ms = new MemoryStream(); + var writerFn = RequestHelper.GetResponseWriter(HttpListenerHost.Instance, contentType); - ms.Position = 0; + writerFn(dto, ms); + + ms.Position = 0; - var responseHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); + if (string.Equals(request.Verb, "head", StringComparison.OrdinalIgnoreCase)) + { + return GetHttpResult(new byte[] { }, contentType, true); + } - return GetCompressedResult(ms, compressionType, responseHeaders, false, request.ResponseContentType).Result; + return GetHttpResult(ms, contentType, true); + } } } - private static Stream GetCompressionStream(Stream outputStream, string compressionType) - { - if (compressionType == "deflate") - return new DeflateStream(outputStream, CompressionMode.Compress, true); - if (compressionType == "gzip") - return new GZipStream(outputStream, CompressionMode.Compress, true); - - throw new NotSupportedException(compressionType); - } - public static string GetRealContentType(string contentType) { return contentType == null @@ -568,123 +552,47 @@ namespace Emby.Server.Implementations.HttpServer var contentType = options.ContentType; var responseHeaders = options.ResponseHeaders; - var requestedCompressionType = GetCompressionType(requestContext); + //var requestedCompressionType = GetCompressionType(requestContext); - if (!compress || string.IsNullOrEmpty(requestedCompressionType)) - { - var rangeHeader = requestContext.Headers.Get("Range"); - - if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) - { - return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) - { - OnComplete = options.OnComplete, - OnError = options.OnError, - FileShare = options.FileShare - }; - } - - if (!string.IsNullOrWhiteSpace(rangeHeader)) - { - var stream = await factoryFn().ConfigureAwait(false); - - return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) - { - OnComplete = options.OnComplete - }; - } - else - { - var stream = await factoryFn().ConfigureAwait(false); - - responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); - - if (isHeadRequest) - { - stream.Dispose(); - - return GetHttpResult(new byte[] { }, contentType, true); - } - - return new StreamWriter(stream, contentType, _logger) - { - OnComplete = options.OnComplete, - OnError = options.OnError - }; - } - } + var rangeHeader = requestContext.Headers.Get("Range"); - using (var stream = await factoryFn().ConfigureAwait(false)) + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { - return await GetCompressedResult(stream, requestedCompressionType, responseHeaders, isHeadRequest, contentType).ConfigureAwait(false); + return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) + { + OnComplete = options.OnComplete, + OnError = options.OnError, + FileShare = options.FileShare + }; } - } - private async Task<IHasHeaders> GetCompressedResult(Stream stream, - string requestedCompressionType, - IDictionary<string, string> responseHeaders, - bool isHeadRequest, - string contentType) - { - using (var reader = new MemoryStream()) + if (!string.IsNullOrWhiteSpace(rangeHeader)) { - await stream.CopyToAsync(reader).ConfigureAwait(false); - - reader.Position = 0; - var content = reader.ToArray(); + var stream = await factoryFn().ConfigureAwait(false); - if (content.Length >= 1024) + return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) { - content = Compress(content, requestedCompressionType); - responseHeaders["Content-Encoding"] = requestedCompressionType; - } + OnComplete = options.OnComplete + }; + } + else + { + var stream = await factoryFn().ConfigureAwait(false); - responseHeaders["Vary"] = "Accept-Encoding"; - responseHeaders["Content-Length"] = content.Length.ToString(UsCulture); + responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); if (isHeadRequest) { + stream.Dispose(); + return GetHttpResult(new byte[] { }, contentType, true); } - return GetHttpResult(content, contentType, true, responseHeaders); - } - } - - private byte[] Compress(byte[] bytes, string compressionType) - { - if (compressionType == "deflate") - return Deflate(bytes); - - if (compressionType == "gzip") - return GZip(bytes); - - throw new NotSupportedException(compressionType); - } - - private byte[] Deflate(byte[] bytes) - { - // In .NET FX incompat-ville, you can't access compressed bytes without closing DeflateStream - // Which means we must use MemoryStream since you have to use ToArray() on a closed Stream - using (var ms = new MemoryStream()) - using (var zipStream = new DeflateStream(ms, CompressionMode.Compress)) - { - zipStream.Write(bytes, 0, bytes.Length); - zipStream.Dispose(); - - return ms.ToArray(); - } - } - - private byte[] GZip(byte[] buffer) - { - using (var ms = new MemoryStream()) - using (var zipStream = new GZipStream(ms, CompressionMode.Compress)) - { - zipStream.Write(buffer, 0, buffer.Length); - zipStream.Dispose(); - - return ms.ToArray(); + return new StreamWriter(stream, contentType, _logger) + { + OnComplete = options.OnComplete, + OnError = options.OnError + }; } } diff --git a/Emby.Server.Implementations/Library/UserViewManager.cs b/Emby.Server.Implementations/Library/UserViewManager.cs index a6ed84f29..a277b693a 100644 --- a/Emby.Server.Implementations/Library/UserViewManager.cs +++ b/Emby.Server.Implementations/Library/UserViewManager.cs @@ -269,7 +269,41 @@ namespace Emby.Server.Implementations.Library return new List<BaseItem>(); } - var excludeItemTypes = includeItemTypes.Length == 0 ? new[] + var mediaTypes = new List<string>(); + + if (includeItemTypes.Length == 0) + { + foreach (var parent in parents.OfType<ICollectionFolder>()) + { + switch (parent.CollectionType) + { + case CollectionType.Books: + mediaTypes.Add(MediaType.Book); + break; + case CollectionType.Games: + mediaTypes.Add(MediaType.Game); + break; + case CollectionType.Music: + mediaTypes.Add(MediaType.Audio); + break; + case CollectionType.Photos: + mediaTypes.Add(MediaType.Photo); + mediaTypes.Add(MediaType.Video); + break; + case CollectionType.HomeVideos: + mediaTypes.Add(MediaType.Photo); + mediaTypes.Add(MediaType.Video); + break; + default: + mediaTypes.Add(MediaType.Video); + break; + } + } + + mediaTypes = mediaTypes.Distinct().ToList(); + } + + var excludeItemTypes = includeItemTypes.Length == 0 && mediaTypes.Count == 0 ? new[] { typeof(Person).Name, typeof(Studio).Name, @@ -290,7 +324,8 @@ namespace Emby.Server.Implementations.Library IsVirtualItem = false, Limit = limit * 5, IsPlayed = isPlayed, - DtoOptions = options + DtoOptions = options, + MediaTypes = mediaTypes.ToArray() }; if (parents.Count == 0) diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 9ac599846..99b5558a2 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1632,7 +1632,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } - var episodesToDelete = (librarySeries.GetItems(new InternalItemsQuery + var episodesToDelete = (librarySeries.GetItemList(new InternalItemsQuery { SortBy = new[] { ItemSortBy.DateCreated }, SortOrder = SortOrder.Descending, @@ -1642,7 +1642,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV DtoOptions = new DtoOptions(true) })) - .Items .Where(i => i.LocationType == LocationType.FileSystem && _fileSystem.FileExists(i.Path)) .Skip(seriesTimer.KeepUpTo - 1) .ToList(); diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 1f9817e20..10aab4054 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1845,6 +1845,9 @@ namespace Emby.Server.Implementations.LiveTv public async Task AddInfoToProgramDto(List<Tuple<BaseItem, BaseItemDto>> tuples, List<ItemFields> fields, User user = null) { var programTuples = new List<Tuple<BaseItemDto, string, string, string>>(); + var hasChannelImage = fields.Contains(ItemFields.ChannelImage); + var hasChannelInfo = fields.Contains(ItemFields.ChannelInfo); + var hasServiceName = fields.Contains(ItemFields.ServiceName); foreach (var tuple in tuples) { @@ -1887,7 +1890,7 @@ namespace Emby.Server.Implementations.LiveTv dto.IsPremiere = program.IsPremiere; } - if (fields.Contains(ItemFields.ChannelInfo)) + if (hasChannelInfo || hasChannelImage) { var channel = GetInternalChannel(program.ChannelId); @@ -1897,7 +1900,7 @@ namespace Emby.Server.Implementations.LiveTv dto.MediaType = channel.MediaType; dto.ChannelNumber = channel.Number; - if (channel.HasImage(ImageType.Primary)) + if (hasChannelImage && channel.HasImage(ImageType.Primary)) { dto.ChannelPrimaryImageTag = _tvDtoService.GetImageTag(channel); } @@ -1906,7 +1909,7 @@ namespace Emby.Server.Implementations.LiveTv var serviceName = program.ServiceName; - if (fields.Contains(ItemFields.ServiceName)) + if (hasServiceName) { dto.ServiceName = serviceName; } diff --git a/Emby.Server.Implementations/Session/SessionManager.cs b/Emby.Server.Implementations/Session/SessionManager.cs index 317f40a37..763ca9f24 100644 --- a/Emby.Server.Implementations/Session/SessionManager.cs +++ b/Emby.Server.Implementations/Session/SessionManager.cs @@ -1089,7 +1089,7 @@ namespace Emby.Server.Implementations.Session { var folder = (Folder)item; - var itemsResult = folder.GetItems(new InternalItemsQuery(user) + var itemsResult = folder.GetItemList(new InternalItemsQuery(user) { Recursive = true, IsFolder = false, @@ -1104,7 +1104,7 @@ namespace Emby.Server.Implementations.Session }); - return FilterToSingleMediaType(itemsResult.Items) + return FilterToSingleMediaType(itemsResult) .OrderBy(i => i.SortName) .ToList(); } diff --git a/Emby.Server.Implementations/TV/TVSeriesManager.cs b/Emby.Server.Implementations/TV/TVSeriesManager.cs index 876c5d58b..03283031e 100644 --- a/Emby.Server.Implementations/TV/TVSeriesManager.cs +++ b/Emby.Server.Implementations/TV/TVSeriesManager.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.TV int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId); + var series = _libraryManager.GetItemById(request.SeriesId) as Series; if (series != null) { @@ -51,17 +51,22 @@ namespace Emby.Server.Implementations.TV } } - if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) + if (!string.IsNullOrWhiteSpace(presentationUniqueKey)) + { + return GetResult(GetNextUpEpisodes(request, user, new[] { presentationUniqueKey }, dtoOptions), request); + } + + if (limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { - IncludeItemTypes = new[] { typeof(Series).Name }, - SortBy = new[] { ItemSortBy.SeriesDatePlayed }, + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.DatePlayed }, SortOrder = SortOrder.Descending, - PresentationUniqueKey = presentationUniqueKey, + SeriesPresentationUniqueKey = presentationUniqueKey, Limit = limit, ParentId = parentIdGuid, Recursive = true, @@ -69,11 +74,12 @@ namespace Emby.Server.Implementations.TV { Fields = new List<ItemFields> { - ItemFields.PresentationUniqueKey + ItemFields.SeriesPresentationUniqueKey } - } + }, + GroupBySeriesPresentationUniqueKey = true - }).Cast<Series>().Select(GetUniqueSeriesKey); + }).Cast<Episode>().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items, dtoOptions); @@ -94,7 +100,7 @@ namespace Emby.Server.Implementations.TV int? limit = null; if (!string.IsNullOrWhiteSpace(request.SeriesId)) { - var series = _libraryManager.GetItemById(request.SeriesId); + var series = _libraryManager.GetItemById(request.SeriesId) as Series; if (series != null) { @@ -103,28 +109,34 @@ namespace Emby.Server.Implementations.TV } } - if (string.IsNullOrWhiteSpace(presentationUniqueKey) && limit.HasValue) + if (!string.IsNullOrWhiteSpace(presentationUniqueKey)) + { + return GetResult(GetNextUpEpisodes(request, user, new [] { presentationUniqueKey }, dtoOptions), request); + } + + if (limit.HasValue) { limit = limit.Value + 10; } var items = _libraryManager.GetItemList(new InternalItemsQuery(user) { - IncludeItemTypes = new[] { typeof(Series).Name }, - SortBy = new[] { ItemSortBy.SeriesDatePlayed }, + IncludeItemTypes = new[] { typeof(Episode).Name }, + SortBy = new[] { ItemSortBy.DatePlayed }, SortOrder = SortOrder.Descending, - PresentationUniqueKey = presentationUniqueKey, + SeriesPresentationUniqueKey = presentationUniqueKey, Limit = limit, DtoOptions = new MediaBrowser.Controller.Dto.DtoOptions { Fields = new List<ItemFields> { - ItemFields.PresentationUniqueKey + ItemFields.SeriesPresentationUniqueKey }, EnableImages = false - } + }, + GroupBySeriesPresentationUniqueKey = true - }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Series>().Select(GetUniqueSeriesKey); + }, parentsFolders.Cast<BaseItem>().ToList()).Cast<Episode>().Select(GetUniqueSeriesKey); // Avoid implicitly captured closure var episodes = GetNextUpEpisodes(request, user, items, dtoOptions); @@ -167,7 +179,12 @@ namespace Emby.Server.Implementations.TV .Where(i => i != null); } - private string GetUniqueSeriesKey(BaseItem series) + private string GetUniqueSeriesKey(Episode episode) + { + return episode.SeriesPresentationUniqueKey; + } + + private string GetUniqueSeriesKey(Series series) { return series.GetPresentationUniqueKey(); } diff --git a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs index f54613384..863391eea 100644 --- a/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/CollectionFolderImageProvider.cs @@ -42,7 +42,7 @@ namespace Emby.Server.Implementations.UserViews var recursive = !new[] { CollectionType.Playlists, CollectionType.Channels }.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase); - var result = view.GetItems(new InternalItemsQuery + var result = view.GetItemList(new InternalItemsQuery { CollapseBoxSetItems = false, Recursive = recursive, @@ -51,7 +51,7 @@ namespace Emby.Server.Implementations.UserViews }); - var items = result.Items.Select(i => + var items = result.Select(i => { var episode = i as Episode; if (episode != null) diff --git a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs index cd2c4728f..5230da8a6 100644 --- a/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs +++ b/Emby.Server.Implementations/UserViews/DynamicImageProvider.cs @@ -70,19 +70,19 @@ namespace Emby.Server.Implementations.UserViews if (string.Equals(view.ViewType, SpecialFolder.MovieGenre, StringComparison.OrdinalIgnoreCase) || string.Equals(view.ViewType, SpecialFolder.TvGenre, StringComparison.OrdinalIgnoreCase)) { - var userItemsResult = view.GetItems(new InternalItemsQuery + var userItemsResult = view.GetItemList(new InternalItemsQuery { CollapseBoxSetItems = false, DtoOptions = new DtoOptions(false) }); - return userItemsResult.Items.ToList(); + return userItemsResult.ToList(); } var isUsingCollectionStrip = IsUsingCollectionStrip(view); var recursive = isUsingCollectionStrip && !new[] { CollectionType.Channels, CollectionType.BoxSets, CollectionType.Playlists }.Contains(view.ViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase); - var result = view.GetItems(new InternalItemsQuery + var result = view.GetItemList(new InternalItemsQuery { User = view.UserId.HasValue ? _userManager.GetUserById(view.UserId.Value) : null, CollapseBoxSetItems = false, @@ -91,7 +91,7 @@ namespace Emby.Server.Implementations.UserViews DtoOptions = new DtoOptions(false) }); - var items = result.Items.Select(i => + var items = result.Select(i => { var episode = i as Episode; if (episode != null) |
