ソースを参照

add new image params

Luke Pulverenti 10 年 前
コミット
d7bdb744ca
44 ファイル変更519 行追加205 行削除
  1. 3 3
      MediaBrowser.Api/Library/LibraryService.cs
  2. 1 1
      MediaBrowser.Api/Movies/MoviesService.cs
  3. 5 2
      MediaBrowser.Api/PackageService.cs
  4. 0 5
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  5. 1 1
      MediaBrowser.Api/PlaylistService.cs
  6. 1 0
      MediaBrowser.Api/StartupWizardService.cs
  7. 1 1
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  8. 6 6
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  9. 46 1
      MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
  10. 3 6
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  11. 1 1
      MediaBrowser.Api/UserLibrary/UserLibraryService.cs
  12. 2 0
      MediaBrowser.Common/Extensions/BaseExtensions.cs
  13. 0 22
      MediaBrowser.Controller/Dlna/DlnaIconResponse.cs
  14. 3 2
      MediaBrowser.Controller/Dlna/IDlnaManager.cs
  15. 15 3
      MediaBrowser.Controller/Dto/IDtoService.cs
  16. 5 1
      MediaBrowser.Controller/Entities/BaseItem.cs
  17. 1 1
      MediaBrowser.Controller/Entities/Folder.cs
  18. 2 1
      MediaBrowser.Controller/Entities/User.cs
  19. 8 0
      MediaBrowser.Controller/Library/ILibraryManager.cs
  20. 14 0
      MediaBrowser.Controller/Library/IUserManager.cs
  21. 4 3
      MediaBrowser.Controller/LiveTv/ILiveTvService.cs
  22. 0 20
      MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs
  23. 0 2
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  24. 82 29
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  25. 11 2
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  26. 3 2
      MediaBrowser.Dlna/DlnaManager.cs
  27. 3 0
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  28. 6 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  29. 6 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  30. 6 0
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  31. 0 1
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  32. 0 3
      MediaBrowser.Model/Dlna/TranscodingProfile.cs
  33. 7 8
      MediaBrowser.Model/Dto/BaseItemDto.cs
  34. 32 0
      MediaBrowser.Model/Dto/DtoOptions.cs
  35. 2 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  36. 25 0
      MediaBrowser.Model/Querying/ItemFields.cs
  37. 11 0
      MediaBrowser.Model/Users/UserPolicy.cs
  38. 35 6
      MediaBrowser.Server.Implementations/Connect/ConnectManager.cs
  39. 99 55
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  40. 38 8
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  41. 3 4
      MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
  42. 14 1
      MediaBrowser.Server.Implementations/Library/UserManager.cs
  43. 13 3
      MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs
  44. 1 1
      MediaBrowser.Server.Implementations/Localization/Server/server.json

+ 3 - 3
MediaBrowser.Api/Library/LibraryService.cs

@@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Library
             var fields = Enum.GetNames(typeof(ItemFields))
                     .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
                     .ToList();
-
+            
             var result = new ItemsResult
             {
                 TotalRecordCount = items.Count,
@@ -353,7 +353,7 @@ namespace MediaBrowser.Api.Library
                     .ToList();
 
             BaseItem parent = item.Parent;
-
+            
             while (parent != null)
             {
                 if (user != null)
@@ -607,7 +607,7 @@ namespace MediaBrowser.Api.Library
                     }
                 }
             }
-
+           
             var dtos = themeSongIds.Select(_libraryManager.GetItemById)
                             .OrderBy(i => i.SortName)
                             .Select(i => _dtoService.GetBaseItemDto(i, fields, user, item));

+ 1 - 1
MediaBrowser.Api/Movies/MoviesService.cs

@@ -208,7 +208,7 @@ namespace MediaBrowser.Api.Movies
             {
                 returnItems = returnItems.Take(request.Limit.Value);
             }
-
+          
             var result = new ItemsResult
             {
                 Items = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray(),

+ 5 - 2
MediaBrowser.Api/PackageService.cs

@@ -16,6 +16,7 @@ namespace MediaBrowser.Api
     /// Class GetPackage
     /// </summary>
     [Route("/Packages/{Name}", "GET", Summary = "Gets a package, by name or assembly guid")]
+    [Authenticated]
     public class GetPackage : IReturn<PackageInfo>
     {
         /// <summary>
@@ -37,6 +38,7 @@ namespace MediaBrowser.Api
     /// Class GetPackages
     /// </summary>
     [Route("/Packages", "GET", Summary = "Gets available packages")]
+    [Authenticated]
     public class GetPackages : IReturn<List<PackageInfo>>
     {
         /// <summary>
@@ -60,6 +62,7 @@ namespace MediaBrowser.Api
     /// Class GetPackageVersionUpdates
     /// </summary>
     [Route("/Packages/Updates", "GET", Summary = "Gets available package updates for currently installed packages")]
+    [Authenticated(Roles = "Admin")]
     public class GetPackageVersionUpdates : IReturn<List<PackageVersionInfo>>
     {
         /// <summary>
@@ -74,6 +77,7 @@ namespace MediaBrowser.Api
     /// Class InstallPackage
     /// </summary>
     [Route("/Packages/Installed/{Name}", "POST", Summary = "Installs a package")]
+    [Authenticated(Roles = "Admin")]
     public class InstallPackage : IReturnVoid
     {
         /// <summary>
@@ -109,6 +113,7 @@ namespace MediaBrowser.Api
     /// Class CancelPackageInstallation
     /// </summary>
     [Route("/Packages/Installing/{Id}", "DELETE", Summary = "Cancels a package installation")]
+    [Authenticated(Roles = "Admin")]
     public class CancelPackageInstallation : IReturnVoid
     {
         /// <summary>
@@ -122,7 +127,6 @@ namespace MediaBrowser.Api
     /// <summary>
     /// Class PackageService
     /// </summary>
-    [Authenticated(Roles = "Admin")]
     public class PackageService : BaseApiService
     {
         private readonly IInstallationManager _installationManager;
@@ -139,7 +143,6 @@ namespace MediaBrowser.Api
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>System.Object.</returns>
-        /// <exception cref="System.ArgumentException">Unsupported PackageType</exception>
         public object Get(GetPackageVersionUpdates request)
         {
             var result = new List<PackageVersionInfo>();

+ 0 - 5
MediaBrowser.Api/Playback/BaseStreamingService.cs

@@ -2011,11 +2011,6 @@ namespace MediaBrowser.Api.Playback
                 state.EstimateContentLength = transcodingProfile.EstimateContentLength;
                 state.EnableMpegtsM2TsMode = transcodingProfile.EnableMpegtsM2TsMode;
                 state.TranscodeSeekInfo = transcodingProfile.TranscodeSeekInfo;
-
-                if (state.VideoRequest != null && string.IsNullOrWhiteSpace(state.VideoRequest.Profile))
-                {
-                    state.VideoRequest.Profile = transcodingProfile.VideoProfile;
-                }
             }
         }
 

+ 1 - 1
MediaBrowser.Api/PlaylistService.cs

@@ -151,7 +151,7 @@ namespace MediaBrowser.Api
             {
                 items = items.Take(request.Limit.Value).ToArray();
             }
-
+            
             var dtos = items
                    .Select(i => _dtoService.GetBaseItemDto(i.Item2, request.GetItemFields().ToList(), user))
                    .ToArray();

+ 1 - 0
MediaBrowser.Api/StartupWizardService.cs

@@ -61,6 +61,7 @@ namespace MediaBrowser.Api
         public void Post(ReportStartupWizardComplete request)
         {
             _config.Configuration.IsStartupWizardCompleted = true;
+            _config.Configuration.EnableLocalizedGuids = true;
             _config.SaveConfiguration();
         }
 

+ 1 - 1
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
             if (request.UserId.HasValue)
             {
                 var user = UserManager.GetUserById(request.UserId.Value);
-
+                
                 return DtoService.GetBaseItemDto(item, fields.ToList(), user);
             }
 

+ 6 - 6
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -127,11 +127,11 @@ namespace MediaBrowser.Api.UserLibrary
 
             }
 
-            var fields = request.GetItemFields().ToList();
-
             var tuples = ibnItems.Select(i => new Tuple<TItemType, List<BaseItem>>(i, i.GetTaggedItems(libraryItems).ToList()));
 
-            var dtos = tuples.Select(i => GetDto(i.Item1, user, fields, i.Item2));
+            var dtoOptions = request.GetDtoOptions();
+
+            var dtos = tuples.Select(i => GetDto(i.Item1, user, dtoOptions, i.Item2));
 
             result.Items = dtos.Where(i => i != null).ToArray();
 
@@ -332,12 +332,12 @@ namespace MediaBrowser.Api.UserLibrary
         /// </summary>
         /// <param name="item">The item.</param>
         /// <param name="user">The user.</param>
-        /// <param name="fields">The fields.</param>
+        /// <param name="options">The options.</param>
         /// <param name="libraryItems">The library items.</param>
         /// <returns>Task{DtoBaseItem}.</returns>
-        private BaseItemDto GetDto(TItemType item, User user, List<ItemFields> fields, List<BaseItem> libraryItems)
+        private BaseItemDto GetDto(TItemType item, User user, DtoOptions options, List<BaseItem> libraryItems)
         {
-            var dto = DtoService.GetItemByNameDto(item, fields, libraryItems, user);
+            var dto = DtoService.GetItemByNameDto(item, options, libraryItems, user);
 
             return dto;
         }

+ 46 - 1
MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Querying;
 using ServiceStack;
 using System;
@@ -9,6 +10,11 @@ namespace MediaBrowser.Api.UserLibrary
 {
     public abstract class BaseItemsRequest : IHasItemFields
     {
+        protected BaseItemsRequest()
+        {
+            EnableImages = true;
+        }
+
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
@@ -116,6 +122,15 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Years", Description = "Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Years { get; set; }
 
+        [ApiMember(Name = "EnableImages", Description = "Optional, include image information in output", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")]
+        public bool EnableImages { get; set; }
+
+        [ApiMember(Name = "ImageTypeLimit", Description = "Optional, the max number of images to return, per image type", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")]
+        public int? ImageTypeLimit { get; set; }
+
+        [ApiMember(Name = "EnableImageTypes", Description = "Optional. The image types to include in the output.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string EnableImageTypes { get; set; }
+
         public string[] GetGenres()
         {
             return (Genres ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
@@ -198,5 +213,35 @@ namespace MediaBrowser.Api.UserLibrary
 
             return val.Split(',');
         }
+
+        public DtoOptions GetDtoOptions()
+        {
+            var options = new DtoOptions();
+
+            options.Fields = this.GetItemFields().ToList();
+            options.EnableImages = EnableImages;
+
+            if (ImageTypeLimit.HasValue)
+            {
+                options.ImageTypeLimit = ImageTypeLimit.Value;
+            }
+
+            if (string.IsNullOrWhiteSpace(EnableImageTypes))
+            {
+                if (options.EnableImages)
+                {
+                    // Get everything
+                    options.ImageTypes = Enum.GetNames(typeof(ImageType))
+                        .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
+                        .ToList();
+                }
+            }
+            else
+            {
+                options.ImageTypes = (EnableImageTypes ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).Select(v => (ImageType)Enum.Parse(typeof(ImageType), v, true)).ToList();
+            }
+
+            return options;
+        }
     }
 }

+ 3 - 6
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -321,15 +321,14 @@ namespace MediaBrowser.Api.UserLibrary
             var result = await GetItemsToSerialize(request, user, parentItem).ConfigureAwait(false);
 
             var isFiltered = result.Item2;
+            var dtoOptions = request.GetDtoOptions();
 
             if (isFiltered)
             {
-                var currentFields = request.GetItemFields().ToList();
-
                 return new ItemsResult
                 {
                     TotalRecordCount = result.Item1.TotalRecordCount,
-                    Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, currentFields, user)).ToArray()
+                    Items = result.Item1.Items.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray()
                 };
             }
 
@@ -363,9 +362,7 @@ namespace MediaBrowser.Api.UserLibrary
 
             var pagedItems = ApplyPaging(request, itemsArray);
 
-            var fields = request.GetItemFields().ToList();
-
-            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)).ToArray();
+            var returnItems = pagedItems.Select(i => _dtoService.GetBaseItemDto(i, dtoOptions, user)).ToArray();
 
             return new ItemsResult
             {

+ 1 - 1
MediaBrowser.Api/UserLibrary/UserLibraryService.cs

@@ -374,7 +374,7 @@ namespace MediaBrowser.Api.UserLibrary
                     item = i.Item1;
                     childCount = i.Item2.Count;
                 }
-
+                
                 var dto = _dtoService.GetBaseItemDto(item, fields, user);
 
                 dto.ChildCount = childCount;

+ 2 - 0
MediaBrowser.Common/Extensions/BaseExtensions.cs

@@ -96,6 +96,8 @@ namespace MediaBrowser.Common.Extensions
         /// <param name="str">The STR.</param>
         /// <param name="type">The type.</param>
         /// <returns>Guid.</returns>
+        /// <exception cref="System.ArgumentNullException">type</exception>
+        [Obsolete("Use LibraryManager.GetNewItemId")]
         public static Guid GetMBId(this string str, Type type)
         {
             if (type == null)

+ 0 - 22
MediaBrowser.Controller/Dlna/DlnaIconResponse.cs

@@ -1,22 +0,0 @@
-using System;
-using System.IO;
-using MediaBrowser.Model.Drawing;
-
-namespace MediaBrowser.Controller.Dlna
-{
-    public class DlnaIconResponse : IDisposable
-    {
-        public Stream Stream { get; set; }
-
-        public ImageFormat Format { get; set; }
-
-        public void Dispose()
-        {
-            if (Stream != null)
-            {
-                Stream.Dispose();
-                Stream = null;
-            }
-        }
-    }
-}

+ 3 - 2
MediaBrowser.Controller/Dlna/IDlnaManager.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Dlna;
+using MediaBrowser.Controller.Drawing;
+using MediaBrowser.Model.Dlna;
 using System.Collections.Generic;
 
 namespace MediaBrowser.Controller.Dlna
@@ -69,6 +70,6 @@ namespace MediaBrowser.Controller.Dlna
         /// </summary>
         /// <param name="filename">The filename.</param>
         /// <returns>DlnaIconResponse.</returns>
-        DlnaIconResponse GetIcon(string filename);
+        ImageStream GetIcon(string filename);
     }
 }

+ 15 - 3
MediaBrowser.Controller/Dto/IDtoService.cs

@@ -22,7 +22,8 @@ namespace MediaBrowser.Controller.Dto
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
-        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item);
+        /// <param name="fields">The fields.</param>
+        void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields);
 
         /// <summary>
         /// Gets the base item dto.
@@ -34,6 +35,16 @@ namespace MediaBrowser.Controller.Dto
         /// <returns>Task{BaseItemDto}.</returns>
         BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null);
 
+        /// <summary>
+        /// Gets the base item dto.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="options">The options.</param>
+        /// <param name="user">The user.</param>
+        /// <param name="owner">The owner.</param>
+        /// <returns>BaseItemDto.</returns>
+        BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null);
+        
         /// <summary>
         /// Gets the chapter information dto.
         /// </summary>
@@ -51,12 +62,13 @@ namespace MediaBrowser.Controller.Dto
         /// <summary>
         /// Gets the item by name dto.
         /// </summary>
+        /// <typeparam name="T"></typeparam>
         /// <param name="item">The item.</param>
-        /// <param name="fields">The fields.</param>
+        /// <param name="options">The options.</param>
         /// <param name="taggedItems">The tagged items.</param>
         /// <param name="user">The user.</param>
         /// <returns>BaseItemDto.</returns>
-        BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
+        BaseItemDto GetItemByNameDto<T>(T item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
             where T : BaseItem, IItemByName;
     }
 }

+ 5 - 1
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1820,7 +1820,11 @@ namespace MediaBrowser.Controller.Entities
                 if (pct > 0)
                 {
                     pct = userData.PlaybackPositionTicks / pct;
-                    dto.PlayedPercentage = 100 * pct;
+
+                    if (pct > 0)
+                    {
+                        dto.PlayedPercentage = 100 * pct;
+                    }
                 }
             }
         }

+ 1 - 1
MediaBrowser.Controller/Entities/Folder.cs

@@ -103,7 +103,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (item.Id == Guid.Empty)
             {
-                item.Id = item.Path.GetMBId(item.GetType());
+                item.Id = LibraryManager.GetNewItemId(item.Path, item.GetType());
             }
 
             if (ActualChildren.Any(i => i.Id == item.Id))

+ 2 - 1
MediaBrowser.Controller/Entities/User.cs

@@ -4,6 +4,7 @@ using MediaBrowser.Controller.Providers;
 using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Connect;
 using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Users;
 using System;
 using System.IO;
 using System.Linq;
@@ -287,7 +288,7 @@ namespace MediaBrowser.Controller.Entities
 
             var localTime = date.ToLocalTime();
 
-            return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) && 
+            return DayOfWeekHelper.GetDaysOfWeek(schedule.DayOfWeek).Contains(localTime.DayOfWeek) &&
                 IsWithinTime(schedule, localTime);
         }
 

+ 8 - 0
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -414,5 +414,13 @@ namespace MediaBrowser.Controller.Library
         IEnumerable<FileSystemInfo> GetAdditionalParts(string file,
             VideoType type,
             IEnumerable<FileSystemInfo> files);
+
+        /// <summary>
+        /// Gets the new item identifier.
+        /// </summary>
+        /// <param name="key">The key.</param>
+        /// <param name="type">The type.</param>
+        /// <returns>Guid.</returns>
+        Guid GetNewItemId(string key, Type type);
     }
 }

+ 14 - 0
MediaBrowser.Controller/Library/IUserManager.cs

@@ -164,5 +164,19 @@ namespace MediaBrowser.Controller.Library
         /// <param name="pin">The pin.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
         Task<PinRedeemResult> RedeemPasswordResetPin(string pin);
+
+        /// <summary>
+        /// Gets the user policy.
+        /// </summary>
+        /// <param name="userId">The user identifier.</param>
+        /// <returns>UserPolicy.</returns>
+        UserPolicy GetUserPolicy(string userId);
+
+        /// <summary>
+        /// Updates the user policy.
+        /// </summary>
+        /// <param name="userId">The user identifier.</param>
+        /// <param name="userPolicy">The user policy.</param>
+        Task UpdateUserPolicy(string userId, UserPolicy userPolicy);
     }
 }

+ 4 - 3
MediaBrowser.Controller/LiveTv/ILiveTvService.cs

@@ -3,6 +3,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Drawing;
 
 namespace MediaBrowser.Controller.LiveTv
 {
@@ -109,7 +110,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="channelId">The channel identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{Stream}.</returns>
-        Task<StreamResponseInfo> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
+        Task<ImageStream> GetChannelImageAsync(string channelId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the recording image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to RecordingInfo
@@ -117,7 +118,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="recordingId">The recording identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{ImageResponseInfo}.</returns>
-        Task<StreamResponseInfo> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
+        Task<ImageStream> GetRecordingImageAsync(string recordingId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the program image asynchronous. This only needs to be implemented if an image path or url cannot be supplied to ProgramInfo
@@ -126,7 +127,7 @@ namespace MediaBrowser.Controller.LiveTv
         /// <param name="channelId">The channel identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{ImageResponseInfo}.</returns>
-        Task<StreamResponseInfo> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
+        Task<ImageStream> GetProgramImageAsync(string programId, string channelId, CancellationToken cancellationToken);
 
         /// <summary>
         /// Gets the recordings asynchronous.

+ 0 - 20
MediaBrowser.Controller/LiveTv/StreamResponseInfo.cs

@@ -1,20 +0,0 @@
-using System.IO;
-using MediaBrowser.Model.Drawing;
-
-namespace MediaBrowser.Controller.LiveTv
-{
-    public class StreamResponseInfo
-    {
-        /// <summary>
-        /// Gets or sets the stream.
-        /// </summary>
-        /// <value>The stream.</value>
-        public Stream Stream { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the MIME.
-        /// </summary>
-        /// <value>The type of the MIME.</value>
-        public ImageFormat Format { get; set; }
-    }
-}

+ 0 - 2
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -105,7 +105,6 @@
     <Compile Include="Devices\IDeviceRepository.cs" />
     <Compile Include="Dlna\ControlRequest.cs" />
     <Compile Include="Dlna\ControlResponse.cs" />
-    <Compile Include="Dlna\DlnaIconResponse.cs" />
     <Compile Include="Dlna\EventSubscriptionResponse.cs" />
     <Compile Include="Dlna\IConnectionManager.cs" />
     <Compile Include="Dlna\IContentDirectory.cs" />
@@ -191,7 +190,6 @@
     <Compile Include="LiveTv\LiveTvException.cs" />
     <Compile Include="LiveTv\LiveTvServiceStatusInfo.cs" />
     <Compile Include="LiveTv\LiveTvTunerInfo.cs" />
-    <Compile Include="LiveTv\StreamResponseInfo.cs" />
     <Compile Include="LiveTv\LiveTvProgram.cs" />
     <Compile Include="LiveTv\LiveTvVideoRecording.cs" />
     <Compile Include="LiveTv\ProgramInfo.cs" />

+ 82 - 29
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -236,7 +236,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 foreach (var i in childrenResult.Items)
                 {
                     var childItem = i.Item;
-                    var displayStubType = GetDisplayStubType(childItem, serverItem.Item);
+                    var displayStubType = i.StubType;
 
                     if (childItem.IsFolder || displayStubType.HasValue)
                     {
@@ -263,29 +263,6 @@ namespace MediaBrowser.Dlna.ContentDirectory
             };
         }
 
-        private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
-        {
-            if (context == null || context.IsFolder)
-            {
-                var movie = item as Movie;
-                if (movie != null)
-                {
-                    if (movie.LocalTrailerIds.Count > 0 ||
-                        movie.SpecialFeatureIds.Count > 0)
-                    {
-                        return StubType.Folder;
-                    }
-
-                    if (movie.People.Count > 0)
-                    {
-                        return StubType.Folder;
-                    }
-                }
-            }
-
-            return null;
-        }
-
         private async Task<IEnumerable<KeyValuePair<string, string>>> HandleSearch(Headers sparams, User user, string deviceId)
         {
             var searchCriteria = new SearchCriteria(sparams.GetValueOrDefault("SearchCriteria", ""));
@@ -418,11 +395,36 @@ namespace MediaBrowser.Dlna.ContentDirectory
         {
             if (stubType.HasValue)
             {
-                var movie = item as Movie;
+                if (stubType.Value == StubType.People)
+                {
+                    var items = item.People.Select(i =>
+                    {
+                        try
+                        {
+                            return _libraryManager.GetPerson(i.Name);
+                        }
+                        catch
+                        {
+                            return null;
+                        }
+
+                    }).Where(i => i != null).ToArray();
+
+                    var result = new QueryResult<ServerItem>
+                    {
+                        Items = items.Select(i => new ServerItem { Item = i }).ToArray(),
+                        TotalRecordCount = items.Length
+                    };
 
-                if (movie != null)
+                    return ApplyPaging(result, startIndex, limit);
+                }
+                if (stubType.Value == StubType.Folder)
                 {
-                    return await GetMovieItems(movie).ConfigureAwait(false);
+                    var movie = item as Movie;
+                    if (movie != null)
+                    {
+                        return ApplyPaging(await GetMovieItems(movie).ConfigureAwait(false), startIndex, limit);
+                    }
                 }
             }
 
@@ -445,13 +447,52 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             }).ConfigureAwait(false);
 
+            var serverItems = queryResult
+                .Items
+                .Select(i => new ServerItem
+                {
+                    Item = i,
+                    StubType = GetDisplayStubType(i, item)
+                })
+                .ToArray();
+
             return new QueryResult<ServerItem>
             {
                 TotalRecordCount = queryResult.TotalRecordCount,
-                Items = queryResult.Items.Select(i => new ServerItem { Item = i, StubType = null }).ToArray()
+                Items = serverItems
             };
         }
 
+        private QueryResult<ServerItem> ApplyPaging(QueryResult<ServerItem> result, int? startIndex, int? limit)
+        {
+            result.Items = result.Items.Skip(startIndex ?? 0).Take(limit ?? int.MaxValue).ToArray();
+
+            return result;
+        }
+
+        private StubType? GetDisplayStubType(BaseItem item, BaseItem context)
+        {
+            if (context == null || context.IsFolder)
+            {
+                var movie = item as Movie;
+                if (movie != null)
+                {
+                    if (movie.LocalTrailerIds.Count > 0 ||
+                        movie.SpecialFeatureIds.Count > 0)
+                    {
+                        return StubType.Folder;
+                    }
+
+                    if (movie.People.Count > 0)
+                    {
+                        return StubType.Folder;
+                    }
+                }
+            }
+
+            return null;
+        }
+
         private Task<QueryResult<ServerItem>> GetMovieItems(Movie item)
         {
             var list = new List<BaseItem>();
@@ -464,6 +505,12 @@ namespace MediaBrowser.Dlna.ContentDirectory
             var serverItems = list.Select(i => new ServerItem { Item = i, StubType = null })
                 .ToList();
 
+            serverItems.Add(new ServerItem
+            {
+                Item = item,
+                StubType = StubType.People
+            });
+
             return Task.FromResult(new QueryResult<ServerItem>
             {
                 Items = serverItems.ToArray(),
@@ -512,6 +559,11 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 stubType = StubType.Folder;
                 id = id.Split(new[] { '_' }, 2)[1];
             }
+            else if (id.StartsWith("people_", StringComparison.OrdinalIgnoreCase))
+            {
+                stubType = StubType.People;
+                id = id.Split(new[] { '_' }, 2)[1];
+            }
 
             if (Guid.TryParse(id, out itemId))
             {
@@ -538,6 +590,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
     public enum StubType
     {
-        Folder = 0
+        Folder = 0,
+        People = 1
     }
 }

+ 11 - 2
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -293,8 +293,17 @@ namespace MediaBrowser.Dlna.Didl
             container.AppendChild(res);
         }
 
-        private string GetDisplayName(BaseItem item, BaseItem context)
+        private string GetDisplayName(BaseItem item, StubType? itemStubType, BaseItem context)
         {
+            if (itemStubType.HasValue && itemStubType.Value == StubType.People)
+            {
+                if (item is Video)
+                {
+                    return _localization.GetLocalizedString("HeaderCastCrew");
+                }
+                return _localization.GetLocalizedString("HeaderPeople");
+            }
+
             var episode = item as Episode;
             var season = context as Season;
 
@@ -491,7 +500,7 @@ namespace MediaBrowser.Dlna.Didl
             // MediaMonkey for example won't display content without a title
             //if (filter.Contains("dc:title"))
             {
-                AddValue(element, "dc", "title", GetDisplayName(item, context), NS_DC);
+                AddValue(element, "dc", "title", GetDisplayName(item, itemStubType, context), NS_DC);
             }
 
             element.AppendChild(CreateObjectClass(element.OwnerDocument, item, itemStubType));

+ 3 - 2
MediaBrowser.Dlna/DlnaManager.cs

@@ -2,6 +2,7 @@
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Dlna;
+using MediaBrowser.Controller.Drawing;
 using MediaBrowser.Controller.Plugins;
 using MediaBrowser.Dlna.Profiles;
 using MediaBrowser.Dlna.Server;
@@ -469,13 +470,13 @@ namespace MediaBrowser.Dlna
             return new DescriptionXmlBuilder(profile, serverUuId, "").GetXml();
         }
 
-        public DlnaIconResponse GetIcon(string filename)
+        public ImageStream GetIcon(string filename)
         {
             var format = filename.EndsWith(".png", StringComparison.OrdinalIgnoreCase)
                 ? ImageFormat.Png
                 : ImageFormat.Jpg;
 
-            return new DlnaIconResponse
+            return new ImageStream
             {
                 Format = format,
                 Stream = GetType().Assembly.GetManifestResourceStream("MediaBrowser.Dlna.Images." + filename.ToLower())

+ 3 - 0
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -299,6 +299,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
                 }
             }
 
+            // TODO: Output in webp for smaller sizes
+            // -f image2 -f webp
+
             // Use ffmpeg to sample 100 (we can drop this if required using thumbnail=50 for 50 frames) frames and pick the best thumbnail. Have a fall back just in case.
             var args = useIFrame ? string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2},thumbnail=30\" -f image2 \"{1}\"", inputPath, "-", vf) :
                 string.Format("-i {0} -threads 0 -v quiet -vframes 1 -vf \"{2}\" -f image2 \"{1}\"", inputPath, "-", vf);

+ 6 - 0
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -422,6 +422,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
       <Link>Dto\ChapterInfoDto.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
+      <Link>Dto\DtoOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
       <Link>Dto\GameSystemSummary.cs</Link>
     </Compile>
@@ -1118,6 +1121,9 @@
     <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
       <Link>Users\PinRedeemResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
+      <Link>Users\UserPolicy.cs</Link>
+    </Compile>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>

+ 6 - 0
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -387,6 +387,9 @@
     <Compile Include="..\MediaBrowser.Model\Dto\ChapterInfoDto.cs">
       <Link>Dto\ChapterInfoDto.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Dto\DtoOptions.cs">
+      <Link>Dto\DtoOptions.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Dto\GameSystemSummary.cs">
       <Link>Dto\GameSystemSummary.cs</Link>
     </Compile>
@@ -1077,6 +1080,9 @@
     <Compile Include="..\MediaBrowser.Model\Users\PinRedeemResult.cs">
       <Link>Users\PinRedeemResult.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Users\UserPolicy.cs">
+      <Link>Users\UserPolicy.cs</Link>
+    </Compile>
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>

+ 6 - 0
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -55,6 +55,12 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
         public bool SaveLocalMeta { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [enable localized guids].
+        /// </summary>
+        /// <value><c>true</c> if [enable localized guids]; otherwise, <c>false</c>.</value>
+        public bool EnableLocalizedGuids { get; set; }
+        
         /// <summary>
         /// Gets or sets the preferred metadata language.
         /// </summary>

+ 0 - 1
MediaBrowser.Model/Dlna/StreamBuilder.cs

@@ -298,7 +298,6 @@ namespace MediaBrowser.Model.Dlna
                 playlistItem.VideoCodec = transcodingProfile.VideoCodec;
                 playlistItem.Protocol = transcodingProfile.Protocol;
                 playlistItem.AudioStreamIndex = audioStreamIndex;
-                playlistItem.VideoProfile = transcodingProfile.VideoProfile;
 
                 List<ProfileCondition> videoTranscodingConditions = new List<ProfileCondition>();
                 foreach (CodecProfile i in options.Profile.CodecProfiles)

+ 0 - 3
MediaBrowser.Model/Dlna/TranscodingProfile.cs

@@ -29,9 +29,6 @@ namespace MediaBrowser.Model.Dlna
         [XmlAttribute("transcodeSeekInfo")]
         public TranscodeSeekInfo TranscodeSeekInfo { get; set; }
 
-        [XmlAttribute("videoProfile")]
-        public string VideoProfile { get; set; }
-
         [XmlAttribute("context")]
         public EncodingContext Context { get; set; }
 

+ 7 - 8
MediaBrowser.Model/Dto/BaseItemDto.cs

@@ -64,7 +64,7 @@ namespace MediaBrowser.Model.Dto
 
         public float? Metascore { get; set; }
 
-        public bool IsUnidentified { get; set; }
+        public bool? IsUnidentified { get; set; }
 
         public int? AnimeSeriesIndex { get; set; }
 
@@ -217,6 +217,12 @@ namespace MediaBrowser.Model.Dto
         /// <value>The run time ticks.</value>
         public long? RunTimeTicks { get; set; }
 
+        /// <summary>
+        /// Gets or sets the recursive unplayed item count.
+        /// </summary>
+        /// <value>The recursive unplayed item count.</value>
+        public int? RecursiveUnplayedItemCount { get; set; }
+
         /// <summary>
         /// Gets or sets the play access.
         /// </summary>
@@ -235,13 +241,6 @@ namespace MediaBrowser.Model.Dto
         /// <value>The production year.</value>
         public int? ProductionYear { get; set; }
 
-        /// <summary>
-        /// Gets or sets the recursive unplayed item count.
-        /// </summary>
-        /// <value>The recursive unplayed item count.</value>
-        [Obsolete]
-        public int? RecursiveUnplayedItemCount { get; set; }
-        
         /// <summary>
         /// Gets or sets the season count.
         /// </summary>

+ 32 - 0
MediaBrowser.Model/Dto/DtoOptions.cs

@@ -0,0 +1,32 @@
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.Dto
+{
+    public class DtoOptions
+    {
+        public List<ItemFields> Fields { get; set; }
+        public List<ImageType> ImageTypes { get; set; }
+        public int ImageTypeLimit { get; set; }
+        public bool EnableImages { get; set; }
+
+        public DtoOptions()
+        {
+            Fields = new List<ItemFields>();
+            ImageTypes = new List<ImageType>();
+            ImageTypeLimit = int.MaxValue;
+            EnableImages = true;
+        }
+
+        public int GetImageLimit(ImageType type)
+        {
+            if (EnableImages && ImageTypes.Contains(type))
+            {
+                return ImageTypeLimit;
+            }
+
+            return 0;
+        }
+    }
+}

+ 2 - 0
MediaBrowser.Model/MediaBrowser.Model.csproj

@@ -125,6 +125,7 @@
     <Compile Include="Dlna\SubtitleDeliveryMethod.cs" />
     <Compile Include="Dlna\SubtitleStreamInfo.cs" />
     <Compile Include="Drawing\ImageOrientation.cs" />
+    <Compile Include="Dto\DtoOptions.cs" />
     <Compile Include="Dto\IHasServerId.cs" />
     <Compile Include="MediaInfo\LiveMediaInfoResult.cs" />
     <Compile Include="Dto\MediaSourceType.cs" />
@@ -412,6 +413,7 @@
     <Compile Include="Users\ForgotPasswordAction.cs" />
     <Compile Include="Users\ForgotPasswordResult.cs" />
     <Compile Include="Users\PinRedeemResult.cs" />
+    <Compile Include="Users\UserPolicy.cs" />
     <None Include="Fody.targets" />
     <None Include="FodyWeavers.xml" />
     <None Include="MediaBrowser.Model.snk" />

+ 25 - 0
MediaBrowser.Model/Querying/ItemFields.cs

@@ -81,11 +81,21 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         Keywords,
 
+        /// <summary>
+        /// The media source count
+        /// </summary>
+        MediaSourceCount,
+
         /// <summary>
         /// The media versions
         /// </summary>
         MediaSources,
 
+        /// <summary>
+        /// The metascore
+        /// </summary>
+        Metascore,
+
         /// <summary>
         /// The metadata settings
         /// </summary>
@@ -101,6 +111,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         ParentId,
 
+        /// <summary>
+        /// The part count
+        /// </summary>
+        PartCount,
+
         /// <summary>
         /// The physical path of the item
         /// </summary>
@@ -126,6 +141,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         PrimaryImageAspectRatio,
 
+        /// <summary>
+        /// The original primary image aspect ratio
+        /// </summary>
+        OriginalPrimaryImageAspectRatio,
+        
         /// <summary>
         /// The revenue
         /// </summary>
@@ -171,6 +191,11 @@ namespace MediaBrowser.Model.Querying
         /// </summary>
         Tags,
 
+        /// <summary>
+        /// The vote count
+        /// </summary>
+        VoteCount,
+
         /// <summary>
         /// The TMDB collection name
         /// </summary>

+ 11 - 0
MediaBrowser.Model/Users/UserPolicy.cs

@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace MediaBrowser.Model.Users
+{
+    public class UserPolicy
+    {
+    }
+}

+ 35 - 6
MediaBrowser.Server.Implementations/Connect/ConnectManager.cs

@@ -88,6 +88,11 @@ namespace MediaBrowser.Server.Implementations.Connect
             }
         }
 
+        private string XApplicationValue
+        {
+            get { return "Media Browser Server/" + _appHost.ApplicationVersion; }
+        }
+
         public ConnectManager(ILogger logger,
             IApplicationPaths appPaths,
             IJsonSerializer json,
@@ -204,9 +209,18 @@ namespace MediaBrowser.Server.Implementations.Connect
                 postData["localAddress"] = localAddress;
             }
 
-            using (var stream = await _httpClient.Post(url, postData, CancellationToken.None).ConfigureAwait(false))
+            var options = new HttpRequestOptions
+            {
+                Url = url,
+                CancellationToken = CancellationToken.None
+            };
+
+            options.SetPostData(postData);
+            SetApplicationHeader(options);
+
+            using (var response = await _httpClient.Post(options).ConfigureAwait(false))
             {
-                var data = _json.DeserializeFromStream<ServerRegistrationResponse>(stream);
+                var data = _json.DeserializeFromStream<ServerRegistrationResponse>(response.Content);
 
                 _data.ServerId = data.Id;
                 _data.AccessKey = data.AccessKey;
@@ -252,6 +266,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             options.SetPostData(postData);
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             // No need to examine the response
             using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
@@ -398,6 +413,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             options.SetPostData(postData);
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             var result = new UserLinkResult();
 
@@ -517,6 +533,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             options.SetPostData(postData);
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             // No need to examine the response
             using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
@@ -562,6 +579,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             };
 
             options.SetPostData(postData);
+            SetApplicationHeader(options);
 
             // No need to examine the response
             using (var stream = (await _httpClient.Post(options).ConfigureAwait(false)).Content)
@@ -629,6 +647,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             };
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
             {
@@ -645,6 +664,11 @@ namespace MediaBrowser.Server.Implementations.Connect
             }
         }
 
+        private void SetApplicationHeader(HttpRequestOptions options)
+        {
+            options.RequestHeaders.Add("X-Application", XApplicationValue);
+        }
+
         private void SetServerAccessToken(HttpRequestOptions options)
         {
             if (string.IsNullOrWhiteSpace(ConnectAccessKey))
@@ -687,6 +711,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             };
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             try
             {
@@ -974,6 +999,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             options.SetPostData(postData);
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             try
             {
@@ -1006,20 +1032,22 @@ namespace MediaBrowser.Server.Implementations.Connect
             {
                 throw new ArgumentNullException("passwordMd5");
             }
-            
-            var request = new HttpRequestOptions
+
+            var options = new HttpRequestOptions
             {
                 Url = GetConnectUrl("user/authenticate")
             };
 
-            request.SetPostData(new Dictionary<string, string>
+            options.SetPostData(new Dictionary<string, string>
                 {
                     {"userName",username},
                     {"password",passwordMd5}
                 });
 
+            SetApplicationHeader(options);
+            
             // No need to examine the response
-            using (var stream = (await _httpClient.SendAsync(request, "POST").ConfigureAwait(false)).Content)
+            using (var response = (await _httpClient.SendAsync(options, "POST").ConfigureAwait(false)).Content)
             {
             }
         }
@@ -1062,6 +1090,7 @@ namespace MediaBrowser.Server.Implementations.Connect
             options.SetPostData(postData);
 
             SetServerAccessToken(options);
+            SetApplicationHeader(options);
 
             try
             {

+ 99 - 55
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -69,7 +69,22 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// <exception cref="System.ArgumentNullException">item</exception>
         public BaseItemDto GetBaseItemDto(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null)
         {
-            var dto = GetBaseItemDtoInternal(item, fields, user, owner);
+            var options = new DtoOptions
+            {
+                Fields = fields
+            };
+
+            // Get everything
+            options.ImageTypes = Enum.GetNames(typeof(ImageType))
+                .Select(i => (ImageType)Enum.Parse(typeof(ImageType), i, true))
+                .ToList();
+            
+            return GetBaseItemDto(item, options, user, owner);
+        }
+
+        public BaseItemDto GetBaseItemDto(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
+        {
+            var dto = GetBaseItemDtoInternal(item, options, user, owner);
 
             var byName = item as IItemByName;
 
@@ -87,8 +102,10 @@ namespace MediaBrowser.Server.Implementations.Dto
             return dto;
         }
 
-        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, List<ItemFields> fields, User user = null, BaseItem owner = null)
+        private BaseItemDto GetBaseItemDtoInternal(BaseItem item, DtoOptions options, User user = null, BaseItem owner = null)
         {
+            var fields = options.Fields;
+
             if (item == null)
             {
                 throw new ArgumentNullException("item");
@@ -115,7 +132,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             {
                 try
                 {
-                    AttachPrimaryImageAspectRatio(dto, item);
+                    AttachPrimaryImageAspectRatio(dto, item, fields);
                 }
                 catch (Exception ex)
                 {
@@ -155,7 +172,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 AttachStudios(dto, item);
             }
 
-            AttachBasicFields(dto, item, owner, fields);
+            AttachBasicFields(dto, item, owner, options);
 
             if (fields.Contains(ItemFields.SyncInfo))
             {
@@ -174,18 +191,19 @@ namespace MediaBrowser.Server.Implementations.Dto
                 }
             }
 
-            if (item is Playlist)
+            var playlist = item as Playlist;
+            if (playlist != null)
             {
-                AttachLinkedChildImages(dto, (Folder)item, user);
+                AttachLinkedChildImages(dto, playlist, user, options);
             }
 
             return dto;
         }
 
-        public BaseItemDto GetItemByNameDto<T>(T item, List<ItemFields> fields, List<BaseItem> taggedItems, User user = null)
+        public BaseItemDto GetItemByNameDto<T>(T item, DtoOptions options, List<BaseItem> taggedItems, User user = null)
             where T : BaseItem, IItemByName
         {
-            var dto = GetBaseItemDtoInternal(item, fields, user);
+            var dto = GetBaseItemDtoInternal(item, options, user);
 
             SetItemByNameInfo(item, dto, taggedItems, user);
 
@@ -369,36 +387,27 @@ namespace MediaBrowser.Server.Implementations.Dto
             dto.GameSystem = item.GameSystemName;
         }
 
-        /// <summary>
-        /// Gets the backdrop image tags.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>List{System.String}.</returns>
-        private List<string> GetBackdropImageTags(BaseItem item)
+        private List<string> GetBackdropImageTags(BaseItem item, int limit)
         {
-            return GetCacheTags(item, ImageType.Backdrop).ToList();
+            return GetCacheTags(item, ImageType.Backdrop, limit).ToList();
         }
 
-        /// <summary>
-        /// Gets the screenshot image tags.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>List{Guid}.</returns>
-        private List<string> GetScreenshotImageTags(BaseItem item)
+        private List<string> GetScreenshotImageTags(BaseItem item, int limit)
         {
             var hasScreenshots = item as IHasScreenshots;
             if (hasScreenshots == null)
             {
                 return new List<string>();
             }
-            return GetCacheTags(item, ImageType.Screenshot).ToList();
+            return GetCacheTags(item, ImageType.Screenshot, limit).ToList();
         }
 
-        private IEnumerable<string> GetCacheTags(BaseItem item, ImageType type)
+        private IEnumerable<string> GetCacheTags(BaseItem item, ImageType type, int limit)
         {
             return item.GetImages(type)
                 .Select(p => GetImageCacheTag(item, p))
                 .Where(i => i != null)
+                .Take(limit)
                 .ToList();
         }
 
@@ -649,9 +658,11 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
         /// <param name="owner">The owner.</param>
-        /// <param name="fields">The fields.</param>
-        private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, List<ItemFields> fields)
+        /// <param name="options">The options.</param>
+        private void AttachBasicFields(BaseItemDto dto, BaseItem item, BaseItem owner, DtoOptions options)
         {
+            var fields = options.Fields;
+
             if (fields.Contains(ItemFields.DateCreated))
             {
                 dto.DateCreated = item.DateCreated;
@@ -662,7 +673,11 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.DisplayMediaType = item.DisplayMediaType;
             }
 
-            dto.IsUnidentified = item.IsUnidentified;
+            // Leave null if false
+            if (item.IsUnidentified)
+            {
+                dto.IsUnidentified = item.IsUnidentified;
+            }
 
             if (fields.Contains(ItemFields.Settings))
             {
@@ -736,10 +751,13 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.AspectRatio = hasAspectRatio.AspectRatio;
             }
 
-            var hasMetascore = item as IHasMetascore;
-            if (hasMetascore != null)
+            if (fields.Contains(ItemFields.ProductionLocations))
             {
-                dto.Metascore = hasMetascore.Metascore;
+                var hasMetascore = item as IHasMetascore;
+                if (hasMetascore != null)
+                {
+                    dto.Metascore = hasMetascore.Metascore;
+                }
             }
 
             if (fields.Contains(ItemFields.AwardSummary))
@@ -751,11 +769,19 @@ namespace MediaBrowser.Server.Implementations.Dto
                 }
             }
 
-            dto.BackdropImageTags = GetBackdropImageTags(item);
+            var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
+            if (backdropLimit > 0)
+            {
+                dto.BackdropImageTags = GetBackdropImageTags(item, backdropLimit);
+            }
 
             if (fields.Contains(ItemFields.ScreenshotImageTags))
             {
-                dto.ScreenshotImageTags = GetScreenshotImageTags(item);
+                var screenshotLimit = options.GetImageLimit(ImageType.Screenshot);
+                if (screenshotLimit > 0)
+                {
+                    dto.ScreenshotImageTags = GetScreenshotImageTags(item, screenshotLimit);
+                }
             }
 
             if (fields.Contains(ItemFields.Genres))
@@ -769,11 +795,14 @@ namespace MediaBrowser.Server.Implementations.Dto
             var currentItem = item;
             foreach (var image in currentItem.ImageInfos.Where(i => !currentItem.AllowsMultipleImages(i.Type)))
             {
-                var tag = GetImageCacheTag(item, image);
-
-                if (tag != null)
+                if (options.GetImageLimit(image.Type) > 0)
                 {
-                    dto.ImageTags[image.Type] = tag;
+                    var tag = GetImageCacheTag(item, image);
+
+                    if (tag != null)
+                    {
+                        dto.ImageTags[image.Type] = tag;
+                    }
                 }
             }
 
@@ -851,14 +880,14 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
 
             // If there are no backdrops, indicate what parent has them in case the Ui wants to allow inheritance
-            if (dto.BackdropImageTags.Count == 0)
+            if (backdropLimit > 0 && dto.BackdropImageTags.Count == 0)
             {
                 var parentWithBackdrop = GetParentBackdropItem(item, owner);
 
                 if (parentWithBackdrop != null)
                 {
                     dto.ParentBackdropItemId = GetDtoId(parentWithBackdrop);
-                    dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop);
+                    dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop, backdropLimit);
                 }
             }
 
@@ -874,7 +903,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             dto.ParentIndexNumber = item.ParentIndexNumber;
 
             // If there is no logo, indicate what parent has one in case the Ui wants to allow inheritance
-            if (!dto.HasLogo)
+            if (!dto.HasLogo && options.GetImageLimit(ImageType.Logo) > 0)
             {
                 var parentWithLogo = GetParentImageItem(item, ImageType.Logo, owner);
 
@@ -887,7 +916,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
 
             // If there is no art, indicate what parent has one in case the Ui wants to allow inheritance
-            if (!dto.HasArtImage)
+            if (!dto.HasArtImage && options.GetImageLimit(ImageType.Thumb) > 0)
             {
                 var parentWithImage = GetParentImageItem(item, ImageType.Art, owner);
 
@@ -900,7 +929,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
 
             // If there is no thumb, indicate what parent has one in case the Ui wants to allow inheritance
-            if (!dto.HasThumb)
+            if (!dto.HasThumb && options.GetImageLimit(ImageType.Thumb) > 0)
             {
                 var parentWithImage = GetParentImageItem(item, ImageType.Thumb, owner);
 
@@ -953,7 +982,11 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             dto.Type = item.GetClientTypeName();
             dto.CommunityRating = item.CommunityRating;
-            dto.VoteCount = item.VoteCount;
+
+            if (fields.Contains(ItemFields.VoteCount))
+            {
+                dto.VoteCount = item.VoteCount;
+            }
 
             if (item.IsFolder)
             {
@@ -1017,8 +1050,15 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.IsoType = video.IsoType;
                 dto.IsHD = video.IsHD;
 
-                dto.PartCount = video.AdditionalPartIds.Count + 1;
-                dto.MediaSourceCount = video.MediaSourceCount;
+                if (fields.Contains(ItemFields.Chapters))
+                {
+                    dto.PartCount = video.AdditionalPartIds.Count + 1;
+                }
+
+                if (fields.Contains(ItemFields.MediaSourceCount))
+                {
+                    dto.MediaSourceCount = video.MediaSourceCount;
+                }
 
                 if (fields.Contains(ItemFields.Chapters))
                 {
@@ -1200,28 +1240,28 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
         }
 
-        private void AttachLinkedChildImages(BaseItemDto dto, Folder folder, User user)
+        private void AttachLinkedChildImages(BaseItemDto dto, Folder folder, User user, DtoOptions options)
         {
             List<BaseItem> linkedChildren = null;
 
-            if (dto.BackdropImageTags.Count == 0)
+            var backdropLimit = options.GetImageLimit(ImageType.Backdrop);
+
+            if (backdropLimit > 0 && dto.BackdropImageTags.Count == 0)
             {
-                if (linkedChildren == null)
-                {
-                    linkedChildren = user == null
-                        ? folder.GetRecursiveChildren().ToList()
-                        : folder.GetRecursiveChildren(user, true).ToList();
-                }
+                linkedChildren = user == null
+                    ? folder.GetRecursiveChildren().ToList()
+                    : folder.GetRecursiveChildren(user, true).ToList();
+
                 var parentWithBackdrop = linkedChildren.FirstOrDefault(i => i.GetImages(ImageType.Backdrop).Any());
 
                 if (parentWithBackdrop != null)
                 {
                     dto.ParentBackdropItemId = GetDtoId(parentWithBackdrop);
-                    dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop);
+                    dto.ParentBackdropImageTags = GetBackdropImageTags(parentWithBackdrop, backdropLimit);
                 }
             }
 
-            if (!dto.ImageTags.ContainsKey(ImageType.Primary))
+            if (!dto.ImageTags.ContainsKey(ImageType.Primary) && options.GetImageLimit(ImageType.Primary) > 0)
             {
                 if (linkedChildren == null)
                 {
@@ -1380,8 +1420,9 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// </summary>
         /// <param name="dto">The dto.</param>
         /// <param name="item">The item.</param>
+        /// <param name="fields">The fields.</param>
         /// <returns>Task.</returns>
-        public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item)
+        public void AttachPrimaryImageAspectRatio(IItemDto dto, IHasImages item, List<ItemFields> fields)
         {
             var imageInfo = item.GetImageInfo(ImageType.Primary, 0);
 
@@ -1412,7 +1453,10 @@ namespace MediaBrowser.Server.Implementations.Dto
                 return;
             }
 
-            dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height;
+            if (fields.Contains(ItemFields.OriginalPrimaryImageAspectRatio))
+            {
+                dto.OriginalPrimaryImageAspectRatio = size.Width / size.Height;
+            }
 
             var supportedEnhancers = _imageProcessor.GetSupportedEnhancers(item, ImageType.Primary).ToList();
 

+ 38 - 8
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -17,7 +17,6 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Naming.Audio;
 using MediaBrowser.Naming.Common;
-using MediaBrowser.Naming.IO;
 using MediaBrowser.Naming.Video;
 using MediaBrowser.Server.Implementations.Library.Resolvers.TV;
 using MediaBrowser.Server.Implementations.Library.Validators;
@@ -485,12 +484,36 @@ namespace MediaBrowser.Server.Implementations.Library
 
             if (item != null)
             {
-                ResolverHelper.SetInitialItemValues(item, args, _fileSystem);
+                ResolverHelper.SetInitialItemValues(item, args, _fileSystem, this);
             }
 
             return item;
         }
 
+        public Guid GetNewItemId(string key, Type type)
+        {
+            if (string.IsNullOrWhiteSpace(key))
+            {
+                throw new ArgumentNullException("key");
+            }
+            if (type == null)
+            {
+                throw new ArgumentNullException("type");
+            }
+
+            if (ConfigurationManager.Configuration.EnableLocalizedGuids && key.StartsWith(ConfigurationManager.ApplicationPaths.ProgramDataPath))
+            {
+                // Try to normalize paths located underneath program-data in an attempt to make them more portable
+                key = key.Substring(ConfigurationManager.ApplicationPaths.ProgramDataPath.Length)
+                    .TrimStart(new[] { '/', '\\' })
+                    .Replace("/", "\\");
+            }
+
+            key = type.FullName + key.ToLower();
+
+            return key.GetMD5();
+        }
+
         public IEnumerable<BaseItem> ReplaceVideosWithPrimaryVersions(IEnumerable<BaseItem> items)
         {
             var dict = new Dictionary<Guid, BaseItem>();
@@ -651,7 +674,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             Directory.CreateDirectory(rootFolderPath);
 
-            var rootFolder = GetItemById(rootFolderPath.GetMBId(typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
+            var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(new DirectoryInfo(rootFolderPath));
 
             // Add in the plug-in folders
             foreach (var child in PluginFolderCreators)
@@ -662,7 +685,14 @@ namespace MediaBrowser.Server.Implementations.Library
                 {
                     if (folder.Id == Guid.Empty)
                     {
-                        folder.Id = (folder.Path ?? folder.GetType().Name).GetMBId(folder.GetType());
+                        if (string.IsNullOrWhiteSpace(folder.Path))
+                        {
+                            folder.Id = GetNewItemId(folder.GetType().Name, folder.GetType());
+                        }
+                        else
+                        {
+                            folder.Id = GetNewItemId(folder.Path, folder.GetType());
+                        }
                     }
 
                     folder = GetItemById(folder.Id) as BasePluginFolder ?? folder;
@@ -685,7 +715,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
                 Directory.CreateDirectory(userRootPath);
 
-                _userRootFolder = GetItemById(userRootPath.GetMBId(typeof(UserRootFolder))) as UserRootFolder ??
+                _userRootFolder = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder ??
                                   (UserRootFolder)ResolvePath(new DirectoryInfo(userRootPath));
             }
 
@@ -801,7 +831,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 Path.Combine(path, validFilename) :
                 Path.Combine(path, subFolderPrefix, validFilename);
 
-            var id = fullPath.GetMBId(type);
+            var id = GetNewItemId(fullPath, type);
 
             BaseItem obj;
 
@@ -1513,7 +1543,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             path = Path.Combine(path, _fileSystem.GetValidFilename(type));
 
-            var id = (path + "_namedview_" + name).GetMBId(typeof(UserView));
+            var id = GetNewItemId(path + "_namedview_" + name, typeof(UserView));
 
             var item = GetItemById(id) as UserView;
 
@@ -1578,7 +1608,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException("viewType");
             }
 
-            var id = ("7_namedview_" + name + user.Id.ToString("N") + parentId).GetMBId(typeof(UserView));
+            var id = GetNewItemId("7_namedview_" + name + user.Id.ToString("N") + parentId, typeof(UserView));
 
             var path = BaseItem.GetInternalMetadataPathForId(id);
 

+ 3 - 4
MediaBrowser.Server.Implementations/Library/ResolverHelper.cs

@@ -1,5 +1,4 @@
-using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using System;
@@ -20,7 +19,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="item">The item.</param>
         /// <param name="args">The args.</param>
         /// <param name="fileSystem">The file system.</param>
-        public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem)
+        public static void SetInitialItemValues(BaseItem item, ItemResolveArgs args, IFileSystem fileSystem, ILibraryManager libraryManager)
         {
             // If the resolver didn't specify this
             if (string.IsNullOrEmpty(item.Path))
@@ -34,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 item.Parent = args.Parent;
             }
 
-            item.Id = item.Path.GetMBId(item.GetType());
+            item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
 
             // If the resolver didn't specify this
             if (string.IsNullOrEmpty(item.DisplayMediaType))

+ 14 - 1
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -17,6 +17,7 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Events;
 using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Serialization;
 using MediaBrowser.Model.Users;
 using System;
@@ -327,7 +328,10 @@ namespace MediaBrowser.Server.Implementations.Library
 
                 try
                 {
-                    _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user);
+                    _dtoServiceFactory().AttachPrimaryImageAspectRatio(dto, user, new List<ItemFields>
+                    {
+                        ItemFields.PrimaryImageAspectRatio
+                    });
                 }
                 catch (Exception ex)
                 {
@@ -765,5 +769,14 @@ namespace MediaBrowser.Server.Implementations.Library
             public DateTime ExpirationDate { get; set; }
         }
 
+        public UserPolicy GetUserPolicy(string userId)
+        {
+            throw new NotImplementedException();
+        }
+
+        public Task UpdateUserPolicy(string userId, UserPolicy userPolicy)
+        {
+            throw new NotImplementedException();
+        }
     }
 }

+ 13 - 3
MediaBrowser.Server.Implementations/LiveTv/LiveTvDtoService.cs

@@ -13,6 +13,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Server.Implementations.LiveTv
 {
@@ -247,7 +248,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             if (imageTag != null)
             {
                 dto.ImageTags[ImageType.Primary] = imageTag;
-                _dtoService.AttachPrimaryImageAspectRatio(dto, recording);
+                _dtoService.AttachPrimaryImageAspectRatio(dto, recording, new List<ItemFields>
+                    {
+                        ItemFields.PrimaryImageAspectRatio
+                    });
             }
 
             if (user != null)
@@ -337,7 +341,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             {
                 dto.ImageTags[ImageType.Primary] = imageTag;
 
-                _dtoService.AttachPrimaryImageAspectRatio(dto, info);
+                _dtoService.AttachPrimaryImageAspectRatio(dto, info, new List<ItemFields>
+                    {
+                        ItemFields.PrimaryImageAspectRatio
+                    });
             }
 
             if (currentProgram != null)
@@ -401,7 +408,10 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             if (imageTag != null)
             {
                 dto.ImageTags[ImageType.Primary] = imageTag;
-                _dtoService.AttachPrimaryImageAspectRatio(dto, item);
+                _dtoService.AttachPrimaryImageAspectRatio(dto, item, new List<ItemFields>
+                    {
+                        ItemFields.PrimaryImageAspectRatio
+                    });
             }
 
             if (user != null)

+ 1 - 1
MediaBrowser.Server.Implementations/Localization/Server/server.json

@@ -542,7 +542,7 @@
     "HeaderRunningTasks": "Running Tasks",
     "HeaderActiveDevices": "Active Devices",
     "HeaderPendingInstallations": "Pending Installations",
-    "HeaerServerInformation": "Server Information",
+    "HeaderServerInformation": "Server Information",
     "ButtonRestartNow": "Restart Now",
     "ButtonRestart": "Restart",
     "ButtonShutdown": "Shutdown",