Browse Source

allow editing of channel images in the web client

Luke Pulverenti 11 years ago
parent
commit
dee2fd5f88

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

@@ -38,6 +38,18 @@ namespace MediaBrowser.Api.Images
         public string Id { get; set; }
     }
 
+    [Route("/LiveTv/Channels/{Id}/Images", "GET")]
+    [Api(Description = "Gets information about an item's images")]
+    public class GetChannelImageInfos : IReturn<List<ImageInfo>>
+    {
+        /// <summary>
+        /// Gets or sets the id.
+        /// </summary>
+        /// <value>The id.</value>
+        [ApiMember(Name = "Id", Description = "Item Id", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "GET")]
+        public string Id { get; set; }
+    }
+
     [Route("/Artists/{Name}/Images", "GET")]
     [Route("/Genres/{Name}/Images", "GET")]
     [Route("/GameGenres/{Name}/Images", "GET")]
@@ -386,6 +398,15 @@ namespace MediaBrowser.Api.Images
             return ToOptimizedResult(result);
         }
 
+        public object Get(GetChannelImageInfos request)
+        {
+            var item = _liveTv.GetChannel(request.Id);
+
+            var result = GetItemImageInfos(item);
+
+            return ToOptimizedResult(result);
+        }
+        
         public object Get(GetItemByNameImageInfos request)
         {
             var result = GetItemByNameImageInfos(request);

+ 38 - 3
MediaBrowser.Api/ItemUpdateService.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.TV;
 using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using ServiceStack.ServiceHost;
@@ -13,6 +14,14 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Api
 {
+    [Route("/LiveTv/Channels/{ChannelId}", "POST")]
+    [Api(("Updates an item"))]
+    public class UpdateChannel : BaseItemDto, IReturnVoid
+    {
+        [ApiMember(Name = "ChannelId", Description = "The id of the channel", IsRequired = true, DataType = "string", ParameterType = "path", Verb = "POST")]
+        public string ChannelId { get; set; }
+    }
+
     [Route("/Items/{ItemId}", "POST")]
     [Api(("Updates an item"))]
     public class UpdateItem : BaseItemDto, IReturnVoid
@@ -73,11 +82,13 @@ namespace MediaBrowser.Api
     {
         private readonly ILibraryManager _libraryManager;
         private readonly IDtoService _dtoService;
+        private readonly ILiveTvManager _liveTv;
 
-        public ItemUpdateService(ILibraryManager libraryManager, IDtoService dtoService)
+        public ItemUpdateService(ILibraryManager libraryManager, IDtoService dtoService, ILiveTvManager liveTv)
         {
             _libraryManager = libraryManager;
             _dtoService = dtoService;
+            _liveTv = liveTv;
         }
 
         public void Post(UpdateItem request)
@@ -87,6 +98,13 @@ namespace MediaBrowser.Api
             Task.WaitAll(task);
         }
 
+        public void Post(UpdateChannel request)
+        {
+            var task = UpdateItem(request);
+
+            Task.WaitAll(task);
+        }
+
         private Task UpdateItem(UpdateItem request)
         {
             var item = _dtoService.GetItemByDtoId(request.ItemId);
@@ -112,6 +130,15 @@ namespace MediaBrowser.Api
             await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
         }
 
+        private async Task UpdateItem(UpdateChannel request)
+        {
+            var item = _liveTv.GetChannel(request.Id);
+
+            UpdateItem(request, item);
+
+            await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
+        }
+
         public void Post(UpdateArtist request)
         {
             var task = UpdateItem(request);
@@ -226,8 +253,16 @@ namespace MediaBrowser.Api
             item.Overview = request.Overview;
             item.Genres = request.Genres;
             item.Tags = request.Tags;
-            item.Studios = request.Studios.Select(x => x.Name).ToList();
-            item.People = request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList();
+
+            if (request.Studios != null)
+            {
+                item.Studios = request.Studios.Select(x => x.Name).ToList();
+            }
+
+            if (request.People != null)
+            {
+                item.People = request.People.Select(x => new PersonInfo { Name = x.Name, Role = x.Role, Type = x.Type }).ToList();
+            }
 
             if (request.DateCreated.HasValue)
             {

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

@@ -61,5 +61,13 @@ namespace MediaBrowser.Controller.LiveTv
 
             return number.ToString("000-") + (Name ?? string.Empty);
         }
+
+        public override string MediaType
+        {
+            get
+            {
+                return ChannelType == ChannelType.Radio ? Model.Entities.MediaType.Audio : Model.Entities.MediaType.Video;
+            }
+        }
     }
 }

+ 6 - 0
MediaBrowser.Model/LiveTv/ChannelInfoDto.cs

@@ -54,5 +54,11 @@ namespace MediaBrowser.Model.LiveTv
         /// </summary>
         /// <value>The type.</value>
         public string Type { get; set; }
+
+        /// <summary>
+        /// Gets or sets the type of the media.
+        /// </summary>
+        /// <value>The type of the media.</value>
+        public string MediaType { get; set; }
     }
 }

+ 104 - 0
MediaBrowser.Providers/LiveTv/ChannelProviderFromXml.cs

@@ -0,0 +1,104 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Logging;
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Providers.LiveTv
+{
+    class ChannelProviderFromXml : BaseMetadataProvider
+    {
+        internal static ChannelProviderFromXml Current { get; private set; }
+        private readonly IFileSystem _fileSystem;
+
+        public ChannelProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem)
+            : base(logManager, configurationManager)
+        {
+            _fileSystem = fileSystem;
+            Current = this;
+        }
+
+        /// <summary>
+        /// Supportses the specified item.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+        public override bool Supports(BaseItem item)
+        {
+            return item is Channel;
+        }
+
+        /// <summary>
+        /// Gets the priority.
+        /// </summary>
+        /// <value>The priority.</value>
+        public override MetadataProviderPriority Priority
+        {
+            get { return MetadataProviderPriority.Second; }
+        }
+
+        private const string XmlFileName = "channel.xml";
+        protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
+        {
+            var xml = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
+
+            if (xml == null)
+            {
+                return false;
+            }
+
+            return _fileSystem.GetLastWriteTimeUtc(xml) > providerInfo.LastRefreshed;
+        }
+
+        /// <summary>
+        /// Fetches metadata and returns true or false indicating if any work that requires persistence was done
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="force">if set to <c>true</c> [force].</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{System.Boolean}.</returns>
+        public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken)
+        {
+            return Fetch(item, cancellationToken);
+        }
+
+        /// <summary>
+        /// Fetches the specified item.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
+        private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken)
+        {
+            cancellationToken.ThrowIfCancellationRequested();
+
+            var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName));
+
+            if (metadataFile != null)
+            {
+                var path = metadataFile.FullName;
+
+                await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+                try
+                {
+                    new BaseItemXmlParser<Channel>(Logger).Fetch((Channel)item, path, cancellationToken);
+                }
+                finally
+                {
+                    XmlParsingResourcePool.Release();
+                }
+
+                SetLastRefreshed(item, DateTime.UtcNow);
+                return true;
+            }
+
+            return false;
+        }
+    }
+}

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

@@ -55,6 +55,7 @@
     <Compile Include="Games\GameSystemProviderFromXml.cs" />
     <Compile Include="ImageFromMediaLocationProvider.cs" />
     <Compile Include="ImagesByNameProvider.cs" />
+    <Compile Include="LiveTv\ChannelProviderFromXml.cs" />
     <Compile Include="MediaInfo\AudioImageProvider.cs" />
     <Compile Include="MediaInfo\BaseFFProbeProvider.cs" />
     <Compile Include="MediaInfo\FFProbeAudioInfoProvider.cs" />
@@ -100,6 +101,7 @@
     <Compile Include="Savers\AlbumXmlSaver.cs" />
     <Compile Include="Savers\ArtistXmlSaver.cs" />
     <Compile Include="Savers\BoxSetXmlSaver.cs" />
+    <Compile Include="Savers\ChannelXmlSaver.cs" />
     <Compile Include="Savers\EpisodeXmlSaver.cs" />
     <Compile Include="Savers\FolderXmlSaver.cs" />
     <Compile Include="Savers\GameSystemXmlSaver.cs" />

+ 74 - 0
MediaBrowser.Providers/Savers/ChannelXmlSaver.cs

@@ -0,0 +1,74 @@
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Providers.LiveTv;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using System.Threading;
+
+namespace MediaBrowser.Providers.Savers
+{
+    /// <summary>
+    /// Class PersonXmlSaver
+    /// </summary>
+    public class ChannelXmlSaver : IMetadataSaver
+    {
+        /// <summary>
+        /// Determines whether [is enabled for] [the specified item].
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="updateType">Type of the update.</param>
+        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
+        public bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
+        {
+            var wasMetadataEdited = (updateType & ItemUpdateType.MetadataEdit) == ItemUpdateType.MetadataEdit;
+            var wasMetadataDownloaded = (updateType & ItemUpdateType.MetadataDownload) == ItemUpdateType.MetadataDownload;
+
+            // If new metadata has been downloaded or metadata was manually edited, proceed
+            if ((wasMetadataEdited || wasMetadataDownloaded))
+            {
+                return item is Channel;
+            }
+
+            return false;
+        }
+
+        /// <summary>
+        /// Saves the specified item.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        public void Save(BaseItem item, CancellationToken cancellationToken)
+        {
+            var builder = new StringBuilder();
+
+            builder.Append("<Item>");
+
+            XmlSaverHelpers.AddCommonNodes(item, builder);
+
+            builder.Append("</Item>");
+
+            var xmlFilePath = GetSavePath(item);
+
+            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
+            {
+            });
+
+            // Set last refreshed so that the provider doesn't trigger after the file save
+            ChannelProviderFromXml.Current.SetLastRefreshed(item, DateTime.UtcNow);
+        }
+
+        /// <summary>
+        /// Gets the save path.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.String.</returns>
+        public string GetSavePath(BaseItem item)
+        {
+            return Path.Combine(item.Path, "channel.xml");
+        }
+    }
+}

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

@@ -74,7 +74,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 Number = info.ChannelNumber,
                 PrimaryImageTag = GetLogoImageTag(info),
                 Type = info.GetType().Name,
-                Id = info.Id.ToString("N")
+                Id = info.Id.ToString("N"),
+                MediaType = info.MediaType
             };
         }
 

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

@@ -479,6 +479,7 @@ namespace MediaBrowser.WebDashboard.Api
                                       "itemgallery.js",
                                       "itemlistpage.js",
                                       "librarysettings.js",
+                                      "livetvchannel.js",
                                       "livetvchannels.js",
                                       "livetvguide.js",
                                       "livetvrecordings.js",

+ 51 - 4
MediaBrowser.WebDashboard/ApiClient.js

@@ -389,6 +389,21 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             });
         };
 
+        self.getLiveTvChannel = function (id) {
+
+            if (!id) {
+                throw new Error("null id");
+            }
+
+            var url = self.getUrl("/LiveTv/Channels/" + id);
+
+            return self.ajax({
+                type: "GET",
+                url: url,
+                dataType: "json"
+            });
+        };
+
         self.getLiveTvChannels = function (options) {
 
             var url = self.getUrl("/LiveTv/Channels", options || {});
@@ -1236,7 +1251,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            } else if (itemType == "Genre") {
+            }
+            else if (itemType == "Channel") {
+                url = self.getUrl("LiveTv/Channels/" + itemId + "/Images");
+            }
+            else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
             } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
@@ -1292,7 +1311,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
-            } else if (itemType == "Genre") {
+            }
+            else if (itemType == "Channel") {
+                url = self.getUrl("LiveTv/Channels/" + itemId + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
+            }
+            else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
             } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images/" + imageType + "/" + imageIndex + "/Index", options);
@@ -1322,7 +1345,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            } else if (itemType == "Genre") {
+            }
+            else if (itemType == "Channel") {
+                url = self.getUrl("LiveTv/Channels/" + itemId + "/Images");
+            }
+            else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
             } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
@@ -1455,7 +1482,11 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
 
             if (itemType == "Artist") {
                 url = self.getUrl("Artists/" + self.encodeName(itemName) + "/Images");
-            } else if (itemType == "Genre") {
+            }
+            else if (itemType == "Channel") {
+                url = self.getUrl("LiveTv/Channels/" + itemId + "/Images");
+            }
+            else if (itemType == "Genre") {
                 url = self.getUrl("Genres/" + self.encodeName(itemName) + "/Images");
             } else if (itemType == "GameGenre") {
                 url = self.getUrl("GameGenres/" + self.encodeName(itemName) + "/Images");
@@ -2318,6 +2349,22 @@ MediaBrowser.ApiClient = function ($, navigator, JSON, WebSocket, setTimeout, wi
             });
         };
 
+        self.updateLiveTvChannel = function (item) {
+
+            if (!item) {
+                throw new Error("null item");
+            }
+
+            var url = self.getUrl("LiveTv/Channels/" + item.Id);
+
+            return self.ajax({
+                type: "POST",
+                url: url,
+                data: JSON.stringify(item),
+                contentType: "application/json"
+            });
+        };
+
         self.updateArtist = function (item) {
 
             if (!item) {

+ 7 - 1
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -80,6 +80,9 @@
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="ApiClient.js" />
+    <Content Include="dashboard-ui\livetvchannel.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\musicalbumartists.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -344,6 +347,9 @@
     <Content Include="dashboard-ui\livetvrecordings.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\livetvchannel.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\musicalbumartists.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -1197,7 +1203,7 @@
     </Content>
   </ItemGroup>
   <ItemGroup>
-    <None Include="packages.config" />
+    <EmbeddedResource Include="packages.config" />
   </ItemGroup>
   <ItemGroup />
   <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />

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