Browse Source

added movie and tv genre pages

Luke Pulverenti 12 years ago
parent
commit
e3f7dcac19

+ 21 - 8
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -62,7 +62,12 @@ namespace MediaBrowser.Api.UserLibrary
                 items = new[] { item };
             }
 
+            items = FilterItems(request, items, user);
+
+            items = ItemsService.ApplySortOrder(request, items, user, LibraryManager);
+            
             var ibnItemsArray = GetAllItems(request, items, user).ToArray();
+      
             IEnumerable<Tuple<string, Func<int>>> ibnItems = ibnItemsArray;
 
             var result = new ItemsResult
@@ -84,7 +89,7 @@ namespace MediaBrowser.Api.UserLibrary
 
             }
 
-            var fields = GetItemFields(request).ToList();
+            var fields = request.GetItemFields().ToList();
 
             var tasks = ibnItems.Select(i => GetDto(i, user, fields));
 
@@ -96,20 +101,28 @@ namespace MediaBrowser.Api.UserLibrary
         }
 
         /// <summary>
-        /// Gets the item fields.
+        /// Filters the items.
         /// </summary>
         /// <param name="request">The request.</param>
-        /// <returns>IEnumerable{ItemFields}.</returns>
-        private IEnumerable<ItemFields> GetItemFields(GetItemsByName request)
+        /// <param name="items">The items.</param>
+        /// <param name="user">The user.</param>
+        /// <returns>IEnumerable{BaseItem}.</returns>
+        private IEnumerable<BaseItem> FilterItems(GetItemsByName request, IEnumerable<BaseItem> items, User user)
         {
-            var val = request.Fields;
+            items = items.AsParallel();
 
-            if (string.IsNullOrEmpty(val))
+            items = ItemsService.ApplyAdditionalFilters(request, items);
+
+            // Apply filters
+            // Run them starting with the ones that are likely to reduce the list the most
+            foreach (var filter in request.GetFilters().OrderByDescending(f => (int)f))
             {
-                return new ItemFields[] { };
+                items = ItemsService.ApplyFilter(items, filter, user, UserManager);
             }
 
-            return val.Split(',').Select(v => (ItemFields)Enum.Parse(typeof(ItemFields), v, true));
+            items = items.AsEnumerable();
+
+            return items;
         }
 
         /// <summary>

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

@@ -1,4 +1,7 @@
-using MediaBrowser.Model.Entities;
+using System.Collections.Generic;
+using System.Linq;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
 using ServiceStack.ServiceHost;
 using System;
 
@@ -13,6 +16,13 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "UserId", Description = "User Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
         public Guid UserId { get; set; }
 
+        /// <summary>
+        /// What to sort the results by
+        /// </summary>
+        /// <value>The sort by.</value>
+        [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, CommunityRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string SortBy { get; set; }
+
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
@@ -54,5 +64,74 @@ namespace MediaBrowser.Api.UserLibrary
         /// <value>The fields.</value>
         [ApiMember(Name = "Fields", Description = "Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: AudioInfo, Chapters, DateCreated, DisplayMediaType, DisplayPreferences, Genres, ItemCounts, IndexOptions, MediaStreams, Overview, OverviewHtml, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, SeriesInfo, SortName, Studios, Taglines, TrailerUrls, UserData", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Fields { get; set; }
+
+        /// <summary>
+        /// Gets or sets the exclude item types.
+        /// </summary>
+        /// <value>The exclude item types.</value>
+        [ApiMember(Name = "ExcludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string ExcludeItemTypes { get; set; }
+
+        /// <summary>
+        /// Gets or sets the include item types.
+        /// </summary>
+        /// <value>The include item types.</value>
+        [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string IncludeItemTypes { get; set; }
+
+        /// <summary>
+        /// Filters to apply to the results
+        /// </summary>
+        /// <value>The filters.</value>
+        [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsRecentlyAdded, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
+        public string Filters { get; set; }
+        
+        /// <summary>
+        /// Gets the filters.
+        /// </summary>
+        /// <returns>IEnumerable{ItemFilter}.</returns>
+        public IEnumerable<ItemFilter> GetFilters()
+        {
+            var val = Filters;
+
+            if (string.IsNullOrEmpty(val))
+            {
+                return new ItemFilter[] { };
+            }
+
+            return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
+        }
+
+        /// <summary>
+        /// Gets the item fields.
+        /// </summary>
+        /// <returns>IEnumerable{ItemFields}.</returns>
+        public IEnumerable<ItemFields> GetItemFields()
+        {
+            var val = Fields;
+
+            if (string.IsNullOrEmpty(val))
+            {
+                return new ItemFields[] { };
+            }
+
+            return val.Split(',').Select(v => (ItemFields)Enum.Parse(typeof(ItemFields), v, true));
+        }
+
+        /// <summary>
+        /// Gets the order by.
+        /// </summary>
+        /// <returns>IEnumerable{ItemSortBy}.</returns>
+        public IEnumerable<string> GetOrderBy()
+        {
+            var val = SortBy;
+
+            if (string.IsNullOrEmpty(val))
+            {
+                return new string[] { };
+            }
+
+            return val.Split(',');
+        }
     }
 }

+ 41 - 112
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -45,20 +45,6 @@ namespace MediaBrowser.Api.UserLibrary
         /// <value>The index by.</value>
         public string IndexBy { get; set; }
 
-        /// <summary>
-        /// What to sort the results by
-        /// </summary>
-        /// <value>The sort by.</value>
-        [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, CommunityRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
-        public string SortBy { get; set; }
-
-        /// <summary>
-        /// Filters to apply to the results
-        /// </summary>
-        /// <value>The filters.</value>
-        [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsRecentlyAdded, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
-        public string Filters { get; set; }
-
         /// <summary>
         /// Limit results to items containing specific genres
         /// </summary>
@@ -73,20 +59,6 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Studios", Description = "Optional. If specified, results will be filtered based on studio. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Studios { get; set; }
 
-        /// <summary>
-        /// Gets or sets the exclude item types.
-        /// </summary>
-        /// <value>The exclude item types.</value>
-        [ApiMember(Name = "ExcludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
-        public string ExcludeItemTypes { get; set; }
-
-        /// <summary>
-        /// Gets or sets the include item types.
-        /// </summary>
-        /// <value>The include item types.</value>
-        [ApiMember(Name = "IncludeItemTypes", Description = "Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
-        public string IncludeItemTypes { get; set; }
-
         /// <summary>
         /// Limit results to items containing specific years
         /// </summary>
@@ -202,22 +174,22 @@ namespace MediaBrowser.Api.UserLibrary
 
             // Apply filters
             // Run them starting with the ones that are likely to reduce the list the most
-            foreach (var filter in GetFilters(request).OrderByDescending(f => (int)f))
+            foreach (var filter in request.GetFilters().OrderByDescending(f => (int)f))
             {
-                items = ApplyFilter(items, filter, user);
+                items = ApplyFilter(items, filter, user, _userManager);
             }
 
             items = items.AsEnumerable();
 
             items = ApplySearchTerm(request, items);
 
-            items = ApplySortOrder(request, items, user);
+            items = ApplySortOrder(request, items, user, _libraryManager);
 
             var itemsArray = items.ToArray();
 
             var pagedItems = ApplyPaging(request, itemsArray);
 
-            var fields = GetItemFields(request).ToList();
+            var fields = request.GetItemFields().ToList();
 
             var dtoBuilder = new DtoBuilder(Logger, _libraryManager, _userManager);
 
@@ -264,12 +236,13 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="request">The request.</param>
         /// <param name="items">The items.</param>
         /// <param name="user">The user.</param>
+        /// <param name="libraryManager">The library manager.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        private IEnumerable<BaseItem> ApplySortOrder(GetItems request, IEnumerable<BaseItem> items, User user)
+        internal static IEnumerable<BaseItem> ApplySortOrder(BaseItemsRequest request, IEnumerable<BaseItem> items, User user, ILibraryManager libraryManager)
         {
-            var orderBy = GetOrderBy(request).ToArray();
+            var orderBy = request.GetOrderBy().ToArray();
 
-            return orderBy.Length == 0 ? items : _libraryManager.Sort(items, user, orderBy, request.SortOrder ?? SortOrder.Ascending);
+            return orderBy.Length == 0 ? items : libraryManager.Sort(items, user, orderBy, request.SortOrder ?? SortOrder.Ascending);
         }
 
         /// <summary>
@@ -278,8 +251,9 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="items">The items.</param>
         /// <param name="filter">The filter.</param>
         /// <param name="user">The user.</param>
+        /// <param name="userManager">The user manager.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        private IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user)
+        internal static IEnumerable<BaseItem> ApplyFilter(IEnumerable<BaseItem> items, ItemFilter filter, User user, IUserManager userManager)
         {
             // Avoids implicitly captured closure
             var currentUser = user;
@@ -289,7 +263,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.Likes:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value;
                     });
@@ -297,7 +271,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.Dislikes:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value;
                     });
@@ -305,7 +279,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.IsFavorite:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata != null && userdata.IsFavorite;
                     });
@@ -316,7 +290,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.IsResumable:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata != null && userdata.PlaybackPositionTicks > 0;
                     });
@@ -324,7 +298,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.IsPlayed:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata != null && userdata.PlayCount > 0;
                     });
@@ -332,7 +306,7 @@ namespace MediaBrowser.Api.UserLibrary
                 case ItemFilter.IsUnplayed:
                     return items.Where(item =>
                     {
-                        var userdata = _userManager.GetUserData(user.Id, item.UserDataId).Result;
+                        var userdata = userManager.GetUserData(user.Id, item.UserDataId).Result;
 
                         return userdata == null || userdata.PlayCount == 0;
                     });
@@ -350,11 +324,32 @@ namespace MediaBrowser.Api.UserLibrary
         /// <summary>
         /// Applies the additional filters.
         /// </summary>
-        /// <param name="request">The request.</param>
+        /// <param name="itemsRequest">The items request.</param>
         /// <param name="items">The items.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        private IEnumerable<BaseItem> ApplyAdditionalFilters(GetItems request, IEnumerable<BaseItem> items)
+        internal static IEnumerable<BaseItem> ApplyAdditionalFilters(BaseItemsRequest itemsRequest, IEnumerable<BaseItem> items)
         {
+            // Exclude item types
+            if (!string.IsNullOrEmpty(itemsRequest.ExcludeItemTypes))
+            {
+                var vals = itemsRequest.ExcludeItemTypes.Split(',');
+                items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+            }
+
+            // Include item types
+            if (!string.IsNullOrEmpty(itemsRequest.IncludeItemTypes))
+            {
+                var vals = itemsRequest.IncludeItemTypes.Split(',');
+                items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
+            }
+            
+            var request = itemsRequest as GetItems;
+
+            if (request == null)
+            {
+                return items;
+            }
+
             // Filter by Series Status
             if (!string.IsNullOrEmpty(request.SeriesStatus))
             {
@@ -400,21 +395,6 @@ namespace MediaBrowser.Api.UserLibrary
                 items = items.Where(item => imageTypes.Any(imageType => HasImage(item, imageType)));
             }
 
-            // Exclude item types
-            var excludeItemTypes = request.ExcludeItemTypes;
-            if (!string.IsNullOrEmpty(excludeItemTypes))
-            {
-                var vals = excludeItemTypes.Split(',');
-                items = items.Where(f => !vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
-            }
-
-            var includeItemTypes = request.IncludeItemTypes;
-            if (!string.IsNullOrEmpty(includeItemTypes))
-            {
-                var vals = includeItemTypes.Split(',');
-                items = items.Where(f => vals.Contains(f.GetType().Name, StringComparer.OrdinalIgnoreCase));
-            }
-
             var genres = request.Genres;
 
             // Apply genre filter
@@ -463,7 +443,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="item">The item.</param>
         /// <param name="imageType">Type of the image.</param>
         /// <returns><c>true</c> if the specified item has image; otherwise, <c>false</c>.</returns>
-        private bool HasImage(BaseItem item, ImageType imageType)
+        private static bool HasImage(BaseItem item, ImageType imageType)
         {
             if (imageType == ImageType.Backdrop)
             {
@@ -531,63 +511,12 @@ namespace MediaBrowser.Api.UserLibrary
             return items;
         }
 
-        /// <summary>
-        /// Gets the filters.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>IEnumerable{ItemFilter}.</returns>
-        private IEnumerable<ItemFilter> GetFilters(GetItems request)
-        {
-            var val = request.Filters;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return new ItemFilter[] { };
-            }
-
-            return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true));
-        }
-
-        /// <summary>
-        /// Gets the item fields.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>IEnumerable{ItemFields}.</returns>
-        private IEnumerable<ItemFields> GetItemFields(GetItems request)
-        {
-            var val = request.Fields;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return new ItemFields[] { };
-            }
-
-            return val.Split(',').Select(v => (ItemFields)Enum.Parse(typeof(ItemFields), v, true));
-        }
-
-        /// <summary>
-        /// Gets the order by.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <returns>IEnumerable{ItemSortBy}.</returns>
-        private IEnumerable<string> GetOrderBy(GetItems request)
-        {
-            var val = request.SortBy;
-
-            if (string.IsNullOrEmpty(val))
-            {
-                return new string[] { };
-            }
-
-            return val.Split(',');
-        }
-
         /// <summary>
         /// Gets the image types.
         /// </summary>
         /// <param name="request">The request.</param>
         /// <returns>IEnumerable{ImageType}.</returns>
-        private IEnumerable<ImageType> GetImageTypes(GetItems request)
+        private static IEnumerable<ImageType> GetImageTypes(GetItems request)
         {
             var val = request.ImageTypes;
 

+ 7 - 5
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -461,12 +461,18 @@ namespace MediaBrowser.WebDashboard.Api
                                       "mediaplayer.js",
                                       "metadataconfigurationpage.js",
                                       "metadataimagespage.js",
+                                      "moviegenres.js",
                                       "movies.js",
                                       "moviesrecommended.js",
+                                      "playlist.js",
                                       "pluginspage.js",
                                       "pluginupdatespage.js",
                                       "scheduledtaskpage.js",
                                       "scheduledtaskspage.js",
+                                      "supporterkeypage.js",
+                                      "supporterpage.js",
+                                      "tvgenres.js",
+                                      "tvseries.js",
                                       "tvrecommended.js",
                                       "tvshows.js",
                                       "updatepasswordpage.js",
@@ -474,11 +480,7 @@ namespace MediaBrowser.WebDashboard.Api
                                       "userprofilespage.js",
                                       "wizardfinishpage.js",
                                       "wizardstartpage.js",
-                                      "wizarduserpage.js",
-                                      "supporterkeypage.js",
-                                      "supporterpage.js",
-                                      "playlist.js",
-                                      "tvseries.js"
+                                      "wizarduserpage.js"
                                   };
 
             var memoryStream = new MemoryStream();

+ 47 - 1
MediaBrowser.WebDashboard/ApiClient.js

@@ -1498,7 +1498,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
         };
 
         /**
-         * Gets items based on a query, typicall for children of a folder
+         * Gets items based on a query, typically for children of a folder
          * @param {String} userId
          * @param {Object} options
          * Options accepts the following properties:
@@ -1529,6 +1529,52 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout) {
             });
         };
 
+        /**
+            Gets genres from an item
+        */
+        self.getGenres = function (userId, options) {
+
+            if (!userId) {
+                throw new Error("null userId");
+            }
+
+            var parentId = options.parentId || "root";
+
+            // Don't put these on the query string
+            delete options.parentId;
+
+            var url = self.getUrl("Users/" + userId + "/Items/" + parentId + "/Genres", options);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
+        /**
+            Gets studios from an item
+        */
+        self.getStudios = function (userId, options) {
+
+            if (!userId) {
+                throw new Error("null userId");
+            }
+
+            var parentId = options.parentId || "root";
+
+            // Don't put these on the query string
+            delete options.parentId;
+
+            var url = self.getUrl("Users/" + userId + "/Items/" + parentId + "/Studios", options);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
         /**
          * Gets local trailers for an item
          */

+ 12 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -198,6 +198,9 @@
     </Content>
   </ItemGroup>
   <ItemGroup>
+    <Content Include="dashboard-ui\moviegenres.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\movies.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -219,6 +222,9 @@
     <Content Include="dashboard-ui\scripts\itembynamedetailpage.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\moviegenres.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\playlist.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -254,6 +260,9 @@
     <Content Include="dashboard-ui\itembynamedetails.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\tvgenres.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\tvrecommended.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -275,6 +284,9 @@
     <Content Include="dashboard-ui\thirdparty\jplayer\jquery.jplayer.min.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\tvgenres.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\tvrecommended.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>

+ 1 - 1
MediaBrowser.WebDashboard/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.71" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.72" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.43" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.9.43" targetFramework="net45" />
 </packages>