فهرست منبع

added live tv timers page

Luke Pulverenti 11 سال پیش
والد
کامیت
1e9ffb83cf

+ 55 - 0
MediaBrowser.Api/Images/ImageService.cs

@@ -269,6 +269,19 @@ namespace MediaBrowser.Api.Images
         public Guid Id { get; set; }
     }
 
+    [Route("/LiveTv/Channels/{Id}/Images/{Type}", "DELETE")]
+    [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "DELETE")]
+    [Api(Description = "Deletes an item image")]
+    public class DeleteChannelImage : DeleteImageRequest, IReturnVoid
+    {
+        /// <summary>
+        /// Gets or sets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        [ApiMember(Name = "Id", Description = "Channel Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "DELETE")]
+        public string Id { get; set; }
+    }
+    
     /// <summary>
     /// Class PostUserImage
     /// </summary>
@@ -344,6 +357,25 @@ namespace MediaBrowser.Api.Images
         public Stream RequestStream { get; set; }
     }
 
+    [Route("/LiveTv/Channels/{Id}/Images/{Type}", "POST")]
+    [Route("/LiveTv/Channels/{Id}/Images/{Type}/{Index}", "POST")]
+    [Api(Description = "Posts an item image")]
+    public class PostChannelImage : DeleteImageRequest, IRequiresRequestStream, IReturnVoid
+    {
+        /// <summary>
+        /// Gets or sets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+        public string Id { get; set; }
+
+        /// <summary>
+        /// The raw Http Request Input Stream
+        /// </summary>
+        /// <value>The request stream.</value>
+        public Stream RequestStream { get; set; }
+    }
+    
     /// <summary>
     /// Class ImageService
     /// </summary>
@@ -622,6 +654,20 @@ namespace MediaBrowser.Api.Images
             Task.WaitAll(task);
         }
 
+        public void Post(PostChannelImage request)
+        {
+            var pathInfo = PathInfo.Parse(RequestContext.PathInfo);
+            var id = pathInfo.GetArgumentValue<string>(2);
+
+            request.Type = (ImageType)Enum.Parse(typeof(ImageType), pathInfo.GetArgumentValue<string>(4), true);
+
+            var item = _liveTv.GetChannel(id);
+
+            var task = PostImage(item, request.RequestStream, request.Type, RequestContext.ContentType);
+
+            Task.WaitAll(task);
+        }
+        
         /// <summary>
         /// Deletes the specified request.
         /// </summary>
@@ -648,6 +694,15 @@ namespace MediaBrowser.Api.Images
             Task.WaitAll(task);
         }
 
+        public void Delete(DeleteChannelImage request)
+        {
+            var item = _liveTv.GetChannel(request.Id);
+
+            var task = item.DeleteImage(request.Type, request.Index);
+
+            Task.WaitAll(task);
+        }
+        
         /// <summary>
         /// Deletes the specified request.
         /// </summary>

+ 23 - 0
MediaBrowser.Api/LiveTv/LiveTvService.cs

@@ -57,6 +57,17 @@ namespace MediaBrowser.Api.LiveTv
         public string ChannelId { get; set; }
     }
 
+    [Route("/LiveTv/Timers", "GET")]
+    [Api(Description = "Gets live tv timers")]
+    public class GetTimers : IReturn<QueryResult<TimerInfoDto>>
+    {
+        [ApiMember(Name = "ServiceName", Description = "Optional filter by service.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ServiceName { get; set; }
+
+        [ApiMember(Name = "ChannelId", Description = "Optional filter by channel id.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
+        public string ChannelId { get; set; }
+    }
+
     [Route("/LiveTv/Programs", "GET")]
     [Api(Description = "Gets available live tv epgs..")]
     public class GetPrograms : IReturn<QueryResult<ProgramInfoDto>>
@@ -153,5 +164,17 @@ namespace MediaBrowser.Api.LiveTv
 
             return ToOptimizedResult(result);
         }
+
+        public object Get(GetTimers request)
+        {
+            var result = _liveTvManager.GetTimers(new TimerQuery
+            {
+                ChannelId = request.ChannelId,
+                ServiceName = request.ServiceName
+
+            }, CancellationToken.None).Result;
+
+            return ToOptimizedResult(result);
+        }
     }
 }

+ 8 - 0
MediaBrowser.Controller/LiveTv/ILiveTvManager.cs

@@ -45,6 +45,14 @@ namespace MediaBrowser.Controller.LiveTv
         /// <returns>QueryResult{RecordingInfoDto}.</returns>
         Task<QueryResult<RecordingInfoDto>> GetRecordings(RecordingQuery query, CancellationToken cancellationToken);
 
+        /// <summary>
+        /// Gets the timers.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{QueryResult{TimerInfoDto}}.</returns>
+        Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken);
+        
         /// <summary>
         /// Gets the channel.
         /// </summary>

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

@@ -251,6 +251,9 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
       <Link>LiveTv\RecordingStatus.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
+      <Link>LiveTv\TimerInfoDto.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
       <Link>Logging\ILogger.cs</Link>
     </Compile>

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

@@ -238,6 +238,9 @@
     <Compile Include="..\MediaBrowser.Model\LiveTv\RecordingStatus.cs">
       <Link>LiveTv\RecordingStatus.cs</Link>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\LiveTv\TimerInfoDto.cs">
+      <Link>LiveTv\TimerInfoDto.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Logging\ILogger.cs">
       <Link>Logging\ILogger.cs</Link>
     </Compile>

+ 15 - 0
MediaBrowser.Model/LiveTv/RecordingQuery.cs

@@ -17,4 +17,19 @@
         /// <value>The name of the service.</value>
         public string ServiceName { get; set; }
     }
+
+    public class TimerQuery
+    {
+        /// <summary>
+        /// Gets or sets the channel identifier.
+        /// </summary>
+        /// <value>The channel identifier.</value>
+        public string ChannelId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the name of the service.
+        /// </summary>
+        /// <value>The name of the service.</value>
+        public string ServiceName { get; set; }
+    }
 }

+ 72 - 0
MediaBrowser.Model/LiveTv/TimerInfoDto.cs

@@ -0,0 +1,72 @@
+using System;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Model.LiveTv
+{
+    public class TimerInfoDto
+    {
+          /// <summary>
+        /// Id of the recording.
+        /// </summary>
+        public string Id { get; set; }
+
+        /// <summary>
+        /// Gets or sets the external identifier.
+        /// </summary>
+        /// <value>The external identifier.</value>
+        public string ExternalId { get; set; }
+        
+        /// <summary>
+        /// ChannelId of the recording.
+        /// </summary>
+        public string ChannelId { get; set; }
+
+        /// <summary>
+        /// ChannelName of the recording.
+        /// </summary>
+        public string ChannelName { get; set; }
+
+        /// <summary>
+        /// Name of the recording.
+        /// </summary>
+        public string Name { get; set; }
+
+        /// <summary>
+        /// Description of the recording.
+        /// </summary>
+        public string Description { get; set; }
+
+        /// <summary>
+        /// The start date of the recording, in UTC.
+        /// </summary>
+        public DateTime StartDate { get; set; }
+
+        /// <summary>
+        /// The end date of the recording, in UTC.
+        /// </summary>
+        public DateTime EndDate { get; set; }
+
+        /// <summary>
+        /// Gets or sets the status.
+        /// </summary>
+        /// <value>The status.</value>
+        public RecordingStatus Status { get; set; }
+
+        /// <summary>
+        /// Gets or sets a value indicating whether this instance is recurring.
+        /// </summary>
+        /// <value><c>true</c> if this instance is recurring; otherwise, <c>false</c>.</value>
+        public bool IsRecurring { get; set; }
+
+        /// <summary>
+        /// Gets or sets the recurring days.
+        /// </summary>
+        /// <value>The recurring days.</value>
+        public List<DayOfWeek> RecurringDays { get; set; }
+
+        public TimerInfoDto()
+        {
+            RecurringDays = new List<DayOfWeek>();
+        }
+  }
+}

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

@@ -66,6 +66,7 @@
     <Compile Include="LiveTv\ProgramQuery.cs" />
     <Compile Include="LiveTv\RecordingQuery.cs" />
     <Compile Include="LiveTv\RecordingStatus.cs" />
+    <Compile Include="LiveTv\TimerInfoDto.cs" />
     <Compile Include="Providers\ImageProviderInfo.cs" />
     <Compile Include="Providers\RemoteImageInfo.cs" />
     <Compile Include="Dto\StudioDto.cs" />

+ 61 - 1
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -417,7 +417,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                     .ToList();
             }
 
-            var returnArray = list.ToArray();
+            var returnArray = list.OrderByDescending(i => i.StartDate)
+                .ToArray();
 
             return new QueryResult<RecordingInfoDto>
             {
@@ -451,5 +452,64 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
             throw new NotImplementedException();
         }
+
+        public async Task<QueryResult<TimerInfoDto>> GetTimers(TimerQuery query, CancellationToken cancellationToken)
+        {
+            var list = new List<TimerInfoDto>();
+
+            foreach (var service in GetServices(query.ServiceName, query.ChannelId))
+            {
+                var timers = await GetTimers(service, cancellationToken).ConfigureAwait(false);
+
+                list.AddRange(timers);
+            }
+
+            if (!string.IsNullOrEmpty(query.ChannelId))
+            {
+                list = list.Where(i => string.Equals(i.ChannelId, query.ChannelId))
+                    .ToList();
+            }
+
+            var returnArray = list.OrderByDescending(i => i.StartDate)
+                .ToArray();
+
+            return new QueryResult<TimerInfoDto>
+            {
+                Items = returnArray,
+                TotalRecordCount = returnArray.Length
+            };
+        }
+
+        private async Task<IEnumerable<TimerInfoDto>> GetTimers(ILiveTvService service, CancellationToken cancellationToken)
+        {
+            var timers = await service.GetTimersAsync(cancellationToken).ConfigureAwait(false);
+
+            return timers.Select(i => GetTimerInfoDto(i, service));
+        }
+
+        private TimerInfoDto GetTimerInfoDto(TimerInfo info, ILiveTvService service)
+        {
+            var id = service.Name + info.ChannelId + info.Id;
+            id = id.GetMD5().ToString("N");
+
+            var dto = new TimerInfoDto
+            {
+                ChannelName = info.ChannelName,
+                Description = info.Description,
+                EndDate = info.EndDate,
+                Name = info.Name,
+                StartDate = info.StartDate,
+                Id = id,
+                ExternalId = info.Id,
+                ChannelId = GetInternalChannelId(service.Name, info.ChannelId).ToString("N"),
+                Status = info.Status,
+                IsRecurring = info.IsRecurring,
+                RecurringDays = info.RecurringDays
+            };
+
+            return dto;
+        }
+
+
     }
 }

+ 10 - 9
MediaBrowser.Server.Implementations/Sorting/AiredEpisodeOrderComparer.cs

@@ -22,7 +22,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
 
                 if (val != 0)
                 {
-                    return val;
+                    //return val;
                 }
             }
 
@@ -49,8 +49,8 @@ namespace MediaBrowser.Server.Implementations.Sorting
 
         private int Compare(Episode x, Episode y)
         {
-            var isXSpecial = (x.ParentIndexNumber ?? -1) == 0;
-            var isYSpecial = (y.ParentIndexNumber ?? -1) == 0;
+            var isXSpecial = (x.PhysicalSeasonNumber ?? -1) == 0;
+            var isYSpecial = (y.PhysicalSeasonNumber ?? -1) == 0;
 
             if (isXSpecial && isYSpecial)
             {
@@ -67,12 +67,12 @@ namespace MediaBrowser.Server.Implementations.Sorting
                 return CompareEpisodeToSpecial(x, y);
             }
 
-            return CompareEpisodeToSpecial(x, y) * -1;
+            return CompareEpisodeToSpecial(y, x) * -1;
         }
 
         private int CompareEpisodeToSpecial(Episode x, Episode y)
         {
-            var xSeason = x.ParentIndexNumber ?? -1;
+            var xSeason = x.PhysicalSeasonNumber ?? -1;
             var ySeason = y.AirsAfterSeasonNumber ?? y.AirsBeforeSeasonNumber ?? -1;
 
             if (xSeason != ySeason)
@@ -85,8 +85,9 @@ namespace MediaBrowser.Server.Implementations.Sorting
             // Compare episode number
 
             // Add 1 to to non-specials to account for AirsBeforeEpisodeNumber
-            var xEpisode = (x.IndexNumber ?? 0) * 1000 + 1;
-            var yEpisode = (y.AirsBeforeEpisodeNumber ?? 0) * 1000;
+            var xEpisode = x.IndexNumber ?? -1;
+            xEpisode++;
+            var yEpisode = y.AirsBeforeEpisodeNumber ?? 10000;
 
             return xEpisode.CompareTo(yEpisode);
         }
@@ -119,8 +120,8 @@ namespace MediaBrowser.Server.Implementations.Sorting
 
         private int CompareEpisodes(Episode x, Episode y)
         {
-            var xValue = ((x.ParentIndexNumber ?? -1) * 1000) + (x.IndexNumber ?? -1);
-            var yValue = ((y.ParentIndexNumber ?? -1) * 1000) + (y.IndexNumber ?? -1);
+            var xValue = ((x.PhysicalSeasonNumber ?? -1) * 1000) + (x.IndexNumber ?? -1);
+            var yValue = ((y.PhysicalSeasonNumber ?? -1) * 1000) + (y.IndexNumber ?? -1);
 
             return xValue.CompareTo(yValue);
         }

+ 3 - 3
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -119,9 +119,9 @@
     <Reference Include="Hardcodet.Wpf.TaskbarNotification">
       <HintPath>..\packages\Hardcodet.Wpf.TaskbarNotification.1.0.4.0\lib\net40\Hardcodet.Wpf.TaskbarNotification.dll</HintPath>
     </Reference>
-    <Reference Include="MediaBrowser.IsoMounter, Version=1.0.5025.12100, Culture=neutral, processorArchitecture=MSIL">
+    <Reference Include="MediaBrowser.IsoMounter, Version=1.0.5079.1480, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\MediaBrowser.IsoMounter.dll</HintPath>
+      <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\MediaBrowser.IsoMounter.dll</HintPath>
     </Reference>
     <Reference Include="NLog, Version=2.1.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
@@ -129,7 +129,7 @@
     </Reference>
     <Reference Include="pfmclrapi, Version=0.0.0.0, Culture=neutral, processorArchitecture=x86">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.61\lib\net45\pfmclrapi.dll</HintPath>
+      <HintPath>..\packages\MediaBrowser.IsoMounting.3.0.65\lib\net45\pfmclrapi.dll</HintPath>
     </Reference>
     <Reference Include="ServiceStack, Version=3.9.60.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>

+ 1 - 1
MediaBrowser.ServerApplication/packages.config

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="Hardcodet.Wpf.TaskbarNotification" version="1.0.4.0" targetFramework="net45" />
-  <package id="MediaBrowser.IsoMounting" version="3.0.61" targetFramework="net45" />
+  <package id="MediaBrowser.IsoMounting" version="3.0.65" targetFramework="net45" />
   <package id="NLog" version="2.1.0" targetFramework="net45" />
   <package id="ServiceStack" version="3.9.62" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />

+ 2 - 1
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -430,7 +430,7 @@ namespace MediaBrowser.WebDashboard.Api
                                 "http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", 
                                 "http://code.jquery.com/mobile/1.3.2/jquery.mobile-1.3.2.min.js",
                                 "scripts/all.js" + versionString,
-                                "thirdparty/jstree1.0fix2/jquery.jstree.js"
+                                "thirdparty/jstree1.0fix3/jquery.jstree.js"
             };
 
             var tags = files.Select(s => string.Format("<script src=\"{0}\"></script>", s)).ToArray();
@@ -483,6 +483,7 @@ namespace MediaBrowser.WebDashboard.Api
                                       "livetvchannels.js",
                                       "livetvguide.js",
                                       "livetvrecordings.js",
+                                      "livetvtimers.js",
                                       "loginpage.js",
                                       "logpage.js",
                                       "medialibrarypage.js",

+ 74 - 5
MediaBrowser.WebDashboard/ApiClient.js

@@ -380,7 +380,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
         self.getLiveTvServices = function (options) {
 
-            var url = self.getUrl("/LiveTv/Services", options || {});
+            var url = self.getUrl("LiveTv/Services", options || {});
 
             return self.ajax({
                 type: "GET",
@@ -395,7 +395,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
                 throw new Error("null id");
             }
 
-            var url = self.getUrl("/LiveTv/Channels/" + id);
+            var url = self.getUrl("LiveTv/Channels/" + id);
 
             return self.ajax({
                 type: "GET",
@@ -406,7 +406,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
         self.getLiveTvChannels = function (options) {
 
-            var url = self.getUrl("/LiveTv/Channels", options || {});
+            var url = self.getUrl("LiveTv/Channels", options || {});
 
             return self.ajax({
                 type: "GET",
@@ -417,7 +417,7 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
         self.getLiveTvPrograms = function (options) {
 
-            var url = self.getUrl("/LiveTv/Programs", options || {});
+            var url = self.getUrl("LiveTv/Programs", options || {});
 
             return self.ajax({
                 type: "GET",
@@ -428,7 +428,76 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
         self.getLiveTvRecordings = function (options) {
 
-            var url = self.getUrl("/LiveTv/Recordings", options || {});
+            var url = self.getUrl("LiveTv/Recordings", options || {});
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
+        self.getLiveTvRecording = function (id) {
+
+            if (!id) {
+                throw new Error("null id");
+            }
+
+            var url = self.getUrl("LiveTv/Recordings/" + id);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
+        self.deleteLiveTvRecording = function (id) {
+
+            if (!id) {
+                throw new Error("null id");
+            }
+
+            var url = self.getUrl("LiveTv/Recordings/" + id);
+
+            return self.ajax({
+                type: "DELETE",
+                url: url
+            });
+        };
+
+        self.cancelLiveTvTimer = function (id) {
+
+            if (!id) {
+                throw new Error("null id");
+            }
+
+            var url = self.getUrl("LiveTv/Timers/" + id);
+
+            return self.ajax({
+                type: "DELETE",
+                url: url
+            });
+        };
+
+        self.getLiveTvTimers = function (options) {
+
+            var url = self.getUrl("LiveTv/Timers", options || {});
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
+        self.getLiveTvTimer = function (id) {
+
+            if (!id) {
+                throw new Error("null id");
+            }
+
+            var url = self.getUrl("LiveTv/Timers/" + id);
 
             return self.ajax({
                 type: "GET",

+ 36 - 36
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -80,9 +80,18 @@
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="ApiClient.js" />
+    <Content Include="dashboard-ui\css\images\editor.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
+    <Content Include="dashboard-ui\css\images\items\detail\tv.png">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\livetvchannel.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\livetvtimers.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\musicalbumartists.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -281,18 +290,6 @@
     <Content Include="dashboard-ui\css\images\userdata\playedon.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\css\images\views\games.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="dashboard-ui\css\images\views\movies.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="dashboard-ui\css\images\views\music.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
-    <Content Include="dashboard-ui\css\images\views\tvshows.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\css\librarybrowser.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -350,6 +347,9 @@
     <Content Include="dashboard-ui\scripts\livetvchannel.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\livetvtimers.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\musicalbumartists.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -595,76 +595,76 @@
     <Content Include="dashboard-ui\thirdparty\html5slider.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\jquery.jstree.js">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\jquery.jstree.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\apple\bg.jpg">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\apple\bg.jpg">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\apple\d.png">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\apple\d.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\apple\dot_for_ie.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\apple\dot_for_ie.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\apple\style.css">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\apple\style.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\apple\throbber.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\apple\throbber.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\classic\d.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\classic\d.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\classic\d.png">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\classic\d.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\classic\dot_for_ie.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\classic\dot_for_ie.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\classic\style.css">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\classic\style.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\classic\throbber.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\classic\throbber.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default-rtl\d.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default-rtl\d.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default-rtl\d.png">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default-rtl\d.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default-rtl\dots.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default-rtl\dots.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default-rtl\style.css">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default-rtl\style.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default-rtl\throbber.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default-rtl\throbber.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default\d.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default\d.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default\d.png">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default\d.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default\style.css">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default\style.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\default\throbber.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\default\throbber.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\mb3\d.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\mb3\d.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\mb3\d.png">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\mb3\d.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\mb3\style.css">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\mb3\style.css">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\thirdparty\jstree1.0fix2\themes\mb3\throbber.gif">
+    <Content Include="dashboard-ui\thirdparty\jstree1.0fix3\themes\mb3\throbber.gif">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\tvgenres.html">

+ 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.199" targetFramework="net45" />
+  <package id="MediaBrowser.ApiClient.Javascript" version="3.0.200" targetFramework="net45" />
   <package id="ServiceStack.Common" version="3.9.62" targetFramework="net45" />
   <package id="ServiceStack.Text" version="3.9.62" targetFramework="net45" />
 </packages>