浏览代码

progress on channels api

Luke Pulverenti 11 年之前
父节点
当前提交
53749f077b
共有 31 个文件被更改,包括 758 次插入48 次删除
  1. 43 0
      MediaBrowser.Api/ChannelService.cs
  2. 1 0
      MediaBrowser.Api/MediaBrowser.Api.csproj
  3. 9 0
      MediaBrowser.Controller/Channels/Channel.cs
  4. 17 0
      MediaBrowser.Controller/Channels/ChannelAudioItem.cs
  5. 7 2
      MediaBrowser.Controller/Channels/ChannelItemInfo.cs
  6. 17 0
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  7. 14 2
      MediaBrowser.Controller/Channels/IChannel.cs
  8. 17 0
      MediaBrowser.Controller/Channels/IChannelItem.cs
  9. 25 3
      MediaBrowser.Controller/Channels/IChannelManager.cs
  10. 15 0
      MediaBrowser.Controller/Dlna/DeviceIdentification.cs
  11. 22 0
      MediaBrowser.Controller/Dlna/DeviceProfile.cs
  12. 1 0
      MediaBrowser.Controller/Dlna/DirectPlayProfile.cs
  13. 22 1
      MediaBrowser.Controller/Dlna/TranscodingProfile.cs
  14. 1 1
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  15. 4 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  16. 27 27
      MediaBrowser.Dlna/DlnaManager.cs
  17. 3 0
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  18. 3 0
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  19. 57 0
      MediaBrowser.Model/Channels/ChannelQuery.cs
  20. 1 0
      MediaBrowser.Model/MediaBrowser.Model.csproj
  21. 32 0
      MediaBrowser.Providers/Channels/ChannelMetadataService.cs
  22. 32 0
      MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs
  23. 0 2
      MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs
  24. 32 0
      MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs
  25. 3 0
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  26. 87 0
      MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs
  27. 257 0
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  28. 1 1
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  29. 2 0
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  30. 5 0
      MediaBrowser.ServerApplication/ApplicationHost.cs
  31. 1 9
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

+ 43 - 0
MediaBrowser.Api/ChannelService.cs

@@ -0,0 +1,43 @@
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Model.Channels;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Querying;
+using ServiceStack;
+using System.Threading;
+
+namespace MediaBrowser.Api
+{
+    [Route("/Channels", "GET")]
+    [Api(("Gets available channels"))]
+    public class GetChannels : IReturn<QueryResult<BaseItemDto>>
+    {
+        public string UserId { get; set; }
+
+        public int? StartIndex { get; set; }
+
+        public int? Limit { get; set; }
+    }
+
+    public class ChannelService : BaseApiService
+    {
+        private readonly IChannelManager _channelManager;
+
+        public ChannelService(IChannelManager channelManager)
+        {
+            _channelManager = channelManager;
+        }
+
+        public object Get(GetChannels request)
+        {
+            var result = _channelManager.GetChannels(new ChannelQuery
+            {
+                Limit = request.Limit,
+                StartIndex = request.StartIndex,
+                UserId = request.UserId,
+
+            }, CancellationToken.None).Result;
+
+            return ToOptimizedResult(result);
+        }
+    }
+}

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

@@ -66,6 +66,7 @@
     <Compile Include="..\SharedVersion.cs">
     <Compile Include="..\SharedVersion.cs">
       <Link>Properties\SharedVersion.cs</Link>
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="ChannelService.cs" />
     <Compile Include="Movies\CollectionService.cs" />
     <Compile Include="Movies\CollectionService.cs" />
     <Compile Include="Music\AlbumsService.cs" />
     <Compile Include="Music\AlbumsService.cs" />
     <Compile Include="AppThemeService.cs" />
     <Compile Include="AppThemeService.cs" />

+ 9 - 0
MediaBrowser.Controller/Channels/Channel.cs

@@ -0,0 +1,9 @@
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Channels
+{
+    public class Channel : BaseItem
+    {
+        public string OriginalChannelName { get; set; }
+    }
+}

+ 17 - 0
MediaBrowser.Controller/Channels/ChannelAudioItem.cs

@@ -0,0 +1,17 @@
+using MediaBrowser.Controller.Entities.Audio;
+
+namespace MediaBrowser.Controller.Channels
+{
+    public class ChannelAudioItem : Audio, IChannelItem
+    {
+        public string ExternalId { get; set; }
+
+        public ChannelItemType ChannelItemType { get; set; }
+
+        public bool IsInfiniteStream { get; set; }
+
+        public ChannelMediaContentType ContentType { get; set; }
+
+        public string OriginalImageUrl { get; set; }
+    }
+}

+ 7 - 2
MediaBrowser.Controller/Channels/ChannelItemInfo.cs

@@ -1,9 +1,11 @@
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Entities;
+using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 
 
 namespace MediaBrowser.Controller.Channels
 namespace MediaBrowser.Controller.Channels
 {
 {
-    public class ChannelItemInfo
+    public class ChannelItemInfo : IHasProviderIds
     {
     {
         public string Name { get; set; }
         public string Name { get; set; }
 
 
@@ -23,18 +25,21 @@ namespace MediaBrowser.Controller.Channels
 
 
         public long? RunTimeTicks { get; set; }
         public long? RunTimeTicks { get; set; }
 
 
-        public bool IsInfinite { get; set; }
+        public bool IsInfiniteStream { get; set; }
         
         
         public string ImageUrl { get; set; }
         public string ImageUrl { get; set; }
 
 
         public ChannelMediaType MediaType { get; set; }
         public ChannelMediaType MediaType { get; set; }
 
 
         public ChannelMediaContentType ContentType { get; set; }
         public ChannelMediaContentType ContentType { get; set; }
+
+        public Dictionary<string, string> ProviderIds { get; set; }
         
         
         public ChannelItemInfo()
         public ChannelItemInfo()
         {
         {
             Genres = new List<string>();
             Genres = new List<string>();
             People = new List<PersonInfo>();
             People = new List<PersonInfo>();
+            ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
         }
         }
     }
     }
 
 

+ 17 - 0
MediaBrowser.Controller/Channels/ChannelVideoItem.cs

@@ -0,0 +1,17 @@
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Channels
+{
+    public class ChannelVideoItem : Video, IChannelItem
+    {
+        public string ExternalId { get; set; }
+
+        public ChannelItemType ChannelItemType { get; set; }
+
+        public bool IsInfiniteStream { get; set; }
+
+        public ChannelMediaContentType ContentType { get; set; }
+
+        public string OriginalImageUrl { get; set; }
+    }
+}

+ 14 - 2
MediaBrowser.Controller/Channels/IChannel.cs

@@ -25,14 +25,21 @@ namespace MediaBrowser.Controller.Channels
         /// <returns>ChannelCapabilities.</returns>
         /// <returns>ChannelCapabilities.</returns>
         ChannelCapabilities GetCapabilities();
         ChannelCapabilities GetCapabilities();
 
 
+        /// <summary>
+        /// Determines whether [is enabled for] [the specified user].
+        /// </summary>
+        /// <param name="user">The user.</param>
+        /// <returns><c>true</c> if [is enabled for] [the specified user]; otherwise, <c>false</c>.</returns>
+        bool IsEnabledFor(User user);
+
         /// <summary>
         /// <summary>
         /// Searches the specified search term.
         /// Searches the specified search term.
         /// </summary>
         /// </summary>
-        /// <param name="searchTerm">The search term.</param>
+        /// <param name="searchInfo">The search information.</param>
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
         /// <returns>Task{IEnumerable{ChannelItemInfo}}.</returns>
-        Task<IEnumerable<ChannelItemInfo>> Search(string searchTerm, User user, CancellationToken cancellationToken);
+        Task<IEnumerable<ChannelItemInfo>> Search(ChannelSearchInfo searchInfo, User user, CancellationToken cancellationToken);
 
 
         /// <summary>
         /// <summary>
         /// Gets the channel items.
         /// Gets the channel items.
@@ -56,4 +63,9 @@ namespace MediaBrowser.Controller.Channels
     {
     {
         public bool CanSearch { get; set; }
         public bool CanSearch { get; set; }
     }
     }
+
+    public class ChannelSearchInfo
+    {
+        public string SearchTerm { get; set; }
+    }
 }
 }

+ 17 - 0
MediaBrowser.Controller/Channels/IChannelItem.cs

@@ -0,0 +1,17 @@
+using MediaBrowser.Controller.Entities;
+
+namespace MediaBrowser.Controller.Channels
+{
+    public interface IChannelItem : IHasImages
+    {
+        string ExternalId { get; set; }
+
+        ChannelItemType ChannelItemType { get; set; }
+
+        bool IsInfiniteStream { get; set; }
+
+        ChannelMediaContentType ContentType { get; set; }
+
+        string OriginalImageUrl { get; set; }
+    }
+}

+ 25 - 3
MediaBrowser.Controller/Channels/IChannelManager.cs

@@ -1,12 +1,34 @@
-using System;
+using MediaBrowser.Model.Channels;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Querying;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Linq;
-using System.Text;
+using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Controller.Channels
 namespace MediaBrowser.Controller.Channels
 {
 {
     public interface IChannelManager
     public interface IChannelManager
     {
     {
+        /// <summary>
+        /// Adds the parts.
+        /// </summary>
+        /// <param name="channels">The channels.</param>
+        void AddParts(IEnumerable<IChannel> channels);
+
+        /// <summary>
+        /// Gets the channels.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
+        Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken);
+
+        /// <summary>
+        /// Gets the channel items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task{QueryResult{BaseItemDto}}.</returns>
+        Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken);
     }
     }
 }
 }

+ 15 - 0
MediaBrowser.Controller/Dlna/DeviceIdentification.cs

@@ -25,6 +25,21 @@ namespace MediaBrowser.Controller.Dlna
         /// <value>The name of the model.</value>
         /// <value>The name of the model.</value>
         public string ModelName { get; set; }
         public string ModelName { get; set; }
         /// <summary>
         /// <summary>
+        /// Gets or sets the model description.
+        /// </summary>
+        /// <value>The model description.</value>
+        public string ModelDescription { get; set; }
+        /// <summary>
+        /// Gets or sets the device description.
+        /// </summary>
+        /// <value>The device description.</value>
+        public string DeviceDescription { get; set; }
+        /// <summary>
+        /// Gets or sets the model URL.
+        /// </summary>
+        /// <value>The model URL.</value>
+        public string ModelUrl { get; set; }
+        /// <summary>
         /// Gets or sets the manufacturer.
         /// Gets or sets the manufacturer.
         /// </summary>
         /// </summary>
         /// <value>
         /// <value>

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

@@ -33,6 +33,28 @@ namespace MediaBrowser.Controller.Dlna
         /// <value>The identification.</value>
         /// <value>The identification.</value>
         public DeviceIdentification Identification { get; set; }
         public DeviceIdentification Identification { get; set; }
 
 
+        public string FriendlyName { get; set; }
+        public string Manufacturer { get; set; }
+        public string ManufacturerUrl { get; set; }
+        public string ModelName { get; set; }
+        public string ModelDescription { get; set; }
+        public string ModelNumber { get; set; }
+        public string ModelUrl { get; set; }
+        /// <summary>
+        /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace.
+        /// </summary>
+        public string XDlnaDoc { get; set; }
+        /// <summary>
+        /// Controls the content of the X_DLNACAP element in the urn:schemas-dlna-org:device-1-0 namespace.
+        /// </summary>
+        public string XDlnaCap { get; set; }
+        /// <summary>
+        /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av.
+        /// </summary>
+        public string SonyAggregationFlags { get; set; }
+
+        public string ProtocolInfo { get; set; }
+
         public DeviceProfile()
         public DeviceProfile()
         {
         {
             DirectPlayProfiles = new DirectPlayProfile[] { };
             DirectPlayProfiles = new DirectPlayProfile[] { };

+ 1 - 0
MediaBrowser.Controller/Dlna/DirectPlayProfile.cs

@@ -53,6 +53,7 @@ namespace MediaBrowser.Controller.Dlna
             }
             }
         }
         }
 
 
+        public string OrgPn { get; set; }
         public string MimeType { get; set; }
         public string MimeType { get; set; }
         public DlnaProfileType Type { get; set; }
         public DlnaProfileType Type { get; set; }
 
 

+ 22 - 1
MediaBrowser.Controller/Dlna/TranscodingProfile.cs

@@ -1,4 +1,5 @@
-
+using System.Collections.Generic;
+
 namespace MediaBrowser.Controller.Dlna
 namespace MediaBrowser.Controller.Dlna
 {
 {
     public class TranscodingProfile
     public class TranscodingProfile
@@ -9,8 +10,28 @@ namespace MediaBrowser.Controller.Dlna
 
 
         public string MimeType { get; set; }
         public string MimeType { get; set; }
 
 
+        public string OrgPn { get; set; }
+
         public string VideoCodec { get; set; }
         public string VideoCodec { get; set; }
 
 
         public string AudioCodec { get; set; }
         public string AudioCodec { get; set; }
+
+        public List<TranscodingSetting> Settings { get; set; }
+
+        public TranscodingProfile()
+        {
+            Settings = new List<TranscodingSetting>();
+        }
+    }
+
+    public class TranscodingSetting
+    {
+        public TranscodingSettingType Name { get; set; }
+        public string Value { get; set; }
+    }
+
+    public enum TranscodingSettingType
+    {
+        Profile
     }
     }
 }
 }

+ 1 - 1
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -107,7 +107,7 @@ namespace MediaBrowser.Controller.LiveTv
 
 
         public override string GetClientTypeName()
         public override string GetClientTypeName()
         {
         {
-            return "Channel";
+            return "TvChannel";
         }
         }
 
 
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)
         public IEnumerable<BaseItem> GetTaggedItems(IEnumerable<BaseItem> inputItems)

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

@@ -71,6 +71,10 @@
     <Compile Include="Channels\ChannelItemInfo.cs" />
     <Compile Include="Channels\ChannelItemInfo.cs" />
     <Compile Include="Channels\IChannel.cs" />
     <Compile Include="Channels\IChannel.cs" />
     <Compile Include="Channels\IChannelManager.cs" />
     <Compile Include="Channels\IChannelManager.cs" />
+    <Compile Include="Channels\IChannelItem.cs" />
+    <Compile Include="Channels\ChannelAudioItem.cs" />
+    <Compile Include="Channels\ChannelVideoItem.cs" />
+    <Compile Include="Channels\Channel.cs" />
     <Compile Include="Collections\CollectionCreationOptions.cs" />
     <Compile Include="Collections\CollectionCreationOptions.cs" />
     <Compile Include="Collections\ICollectionManager.cs" />
     <Compile Include="Collections\ICollectionManager.cs" />
     <Compile Include="Dlna\DeviceIdentification.cs" />
     <Compile Include="Dlna\DeviceIdentification.cs" />

+ 27 - 27
MediaBrowser.Dlna/DlnaManager.cs

@@ -26,8 +26,6 @@ namespace MediaBrowser.Dlna
         {
         {
             var list = new List<DeviceProfile>();
             var list = new List<DeviceProfile>();
 
 
-            #region Samsung
-
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
             {
             {
                 Name = "Samsung TV (B Series)",
                 Name = "Samsung TV (B Series)",
@@ -79,8 +77,6 @@ namespace MediaBrowser.Dlna
                         Type = DlnaProfileType.Video
                         Type = DlnaProfileType.Video
                     }
                     }
                 }
                 }
-                
-
             });
             });
 
 
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
@@ -187,10 +183,6 @@ namespace MediaBrowser.Dlna
                 }
                 }
             });
             });
 
 
-            #endregion
-
-            #region Xbox
-
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
             {
             {
                 Name = "Xbox 360",
                 Name = "Xbox 360",
@@ -272,10 +264,6 @@ namespace MediaBrowser.Dlna
                 }
                 }
             });
             });
 
 
-            #endregion
-
-            #region Sony
-
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
             {
             {
                 Name = "Sony Bravia (2012)",
                 Name = "Sony Bravia (2012)",
@@ -369,10 +357,6 @@ namespace MediaBrowser.Dlna
                 }
                 }
             });
             });
 
 
-            #endregion
-
-            #region Panasonic
-
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
             {
             {
                 //Panasonic Viera (2011|2012) Without AVI Support
                 //Panasonic Viera (2011|2012) Without AVI Support
@@ -461,8 +445,6 @@ namespace MediaBrowser.Dlna
                 }
                 }
             });
             });
 
 
-            #endregion
-
             //WDTV does not need any transcoding of the formats we support statically
             //WDTV does not need any transcoding of the formats we support statically
             list.Add(new DeviceProfile
             list.Add(new DeviceProfile
             {
             {
@@ -625,37 +607,55 @@ namespace MediaBrowser.Dlna
 
 
         private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
         private bool IsMatch(DeviceIdentification deviceInfo, DeviceIdentification profileInfo)
         {
         {
-            if (!string.IsNullOrEmpty(profileInfo.FriendlyName))
+            if (!string.IsNullOrWhiteSpace(profileInfo.DeviceDescription))
+            {
+                if (!Regex.IsMatch(deviceInfo.DeviceDescription, profileInfo.DeviceDescription))
+                    return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(profileInfo.FriendlyName))
             {
             {
                 if (!Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
                 if (!Regex.IsMatch(deviceInfo.FriendlyName, profileInfo.FriendlyName))
                     return false;
                     return false;
             }
             }
 
 
-            if (!string.IsNullOrEmpty(profileInfo.ModelNumber))
+            if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer))
             {
             {
-                if (!Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
+                if (!Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
                     return false;
                     return false;
             }
             }
 
 
-            if (!string.IsNullOrEmpty(profileInfo.ModelName))
+            if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl))
+            {
+                if (!Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
+                    return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(profileInfo.ModelDescription))
+            {
+                if (!Regex.IsMatch(deviceInfo.ModelDescription, profileInfo.ModelDescription))
+                    return false;
+            }
+
+            if (!string.IsNullOrWhiteSpace(profileInfo.ModelName))
             {
             {
                 if (!Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
                 if (!Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName))
                     return false;
                     return false;
             }
             }
 
 
-            if (!string.IsNullOrEmpty(profileInfo.Manufacturer))
+            if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber))
             {
             {
-                if (!Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer))
+                if (!Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber))
                     return false;
                     return false;
             }
             }
 
 
-            if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl))
+            if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl))
             {
             {
-                if (!Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl))
+                if (!Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl))
                     return false;
                     return false;
             }
             }
 
 
-            if (!string.IsNullOrEmpty(profileInfo.SerialNumber))
+            if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber))
             {
             {
                 if (!Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
                 if (!Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber))
                     return false;
                     return false;

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

@@ -74,6 +74,9 @@
     <Compile Include="..\MediaBrowser.Model\ApiClient\ServerEventArgs.cs">
     <Compile Include="..\MediaBrowser.Model\ApiClient\ServerEventArgs.cs">
       <Link>ApiClient\ServerEventArgs.cs</Link>
       <Link>ApiClient\ServerEventArgs.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Channels\ChannelQuery.cs">
+      <Link>Channels\ChannelQuery.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\AutoOrganize.cs">
     <Compile Include="..\MediaBrowser.Model\Configuration\AutoOrganize.cs">
       <Link>Configuration\AutoOrganize.cs</Link>
       <Link>Configuration\AutoOrganize.cs</Link>
     </Compile>
     </Compile>

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

@@ -61,6 +61,9 @@
     <Compile Include="..\MediaBrowser.Model\ApiClient\ServerEventArgs.cs">
     <Compile Include="..\MediaBrowser.Model\ApiClient\ServerEventArgs.cs">
       <Link>ApiClient\ServerEventArgs.cs</Link>
       <Link>ApiClient\ServerEventArgs.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\MediaBrowser.Model\Channels\ChannelQuery.cs">
+      <Link>Channels\ChannelQuery.cs</Link>
+    </Compile>
     <Compile Include="..\MediaBrowser.Model\Configuration\AutoOrganize.cs">
     <Compile Include="..\MediaBrowser.Model\Configuration\AutoOrganize.cs">
       <Link>Configuration\AutoOrganize.cs</Link>
       <Link>Configuration\AutoOrganize.cs</Link>
     </Compile>
     </Compile>

+ 57 - 0
MediaBrowser.Model/Channels/ChannelQuery.cs

@@ -0,0 +1,57 @@
+
+namespace MediaBrowser.Model.Channels
+{
+    public class ChannelQuery
+    {
+        /// <summary>
+        /// Gets or sets the user identifier.
+        /// </summary>
+        /// <value>The user identifier.</value>
+        public string UserId { get; set; }
+
+        /// <summary>
+        /// Skips over a given number of items within the results. Use for paging.
+        /// </summary>
+        /// <value>The start index.</value>
+        public int? StartIndex { get; set; }
+
+        /// <summary>
+        /// The maximum number of items to return
+        /// </summary>
+        /// <value>The limit.</value>
+        public int? Limit { get; set; }
+    }
+
+    public class ChannelItemQuery
+    {
+        /// <summary>
+        /// Gets or sets the channel identifier.
+        /// </summary>
+        /// <value>The channel identifier.</value>
+        public string ChannelId { get; set; }
+
+        /// <summary>
+        /// Gets or sets the category identifier.
+        /// </summary>
+        /// <value>The category identifier.</value>
+        public string CategoryId { get; set; }
+        
+        /// <summary>
+        /// Gets or sets the user identifier.
+        /// </summary>
+        /// <value>The user identifier.</value>
+        public string UserId { get; set; }
+
+        /// <summary>
+        /// Skips over a given number of items within the results. Use for paging.
+        /// </summary>
+        /// <value>The start index.</value>
+        public int? StartIndex { get; set; }
+
+        /// <summary>
+        /// The maximum number of items to return
+        /// </summary>
+        /// <value>The limit.</value>
+        public int? Limit { get; set; }
+    }
+}

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

@@ -58,6 +58,7 @@
     <Compile Include="ApiClient\ApiClientExtensions.cs" />
     <Compile Include="ApiClient\ApiClientExtensions.cs" />
     <Compile Include="ApiClient\IServerEvents.cs" />
     <Compile Include="ApiClient\IServerEvents.cs" />
     <Compile Include="ApiClient\ServerEventArgs.cs" />
     <Compile Include="ApiClient\ServerEventArgs.cs" />
+    <Compile Include="Channels\ChannelQuery.cs" />
     <Compile Include="Configuration\AutoOrganize.cs" />
     <Compile Include="Configuration\AutoOrganize.cs" />
     <Compile Include="Configuration\BaseApplicationConfiguration.cs" />
     <Compile Include="Configuration\BaseApplicationConfiguration.cs" />
     <Compile Include="Configuration\DlnaOptions.cs" />
     <Compile Include="Configuration\DlnaOptions.cs" />

+ 32 - 0
MediaBrowser.Providers/Channels/ChannelMetadataService.cs

@@ -0,0 +1,32 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.Channels
+{
+    public class ChannelMetadataService : MetadataService<Channel, ItemLookupInfo>
+    {
+        public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem)
+            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+        {
+        }
+
+        /// <summary>
+        /// Merges the specified source.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <param name="target">The target.</param>
+        /// <param name="lockedFields">The locked fields.</param>
+        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+        protected override void MergeData(Channel source, Channel target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
+    }
+}

+ 32 - 0
MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs

@@ -0,0 +1,32 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.GameGenres
+{
+    public class AudioChannelItemMetadataService : MetadataService<ChannelAudioItem, ItemLookupInfo>
+    {
+        public AudioChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem)
+            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+        {
+        }
+
+        /// <summary>
+        /// Merges the specified source.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <param name="target">The target.</param>
+        /// <param name="lockedFields">The locked fields.</param>
+        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+        protected override void MergeData(ChannelAudioItem source, ChannelAudioItem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
+    }
+}

+ 0 - 2
MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs

@@ -7,8 +7,6 @@ using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Providers.Manager;
 using MediaBrowser.Providers.Manager;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Threading;
-using System.Threading.Tasks;
 
 
 namespace MediaBrowser.Providers.GameGenres
 namespace MediaBrowser.Providers.GameGenres
 {
 {

+ 32 - 0
MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs

@@ -0,0 +1,32 @@
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Providers.Manager;
+using System.Collections.Generic;
+
+namespace MediaBrowser.Providers.GameGenres
+{
+    public class VideoChannelItemMetadataService : MetadataService<ChannelVideoItem, ItemLookupInfo>
+    {
+        public VideoChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem)
+            : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem)
+        {
+        }
+
+        /// <summary>
+        /// Merges the specified source.
+        /// </summary>
+        /// <param name="source">The source.</param>
+        /// <param name="target">The target.</param>
+        /// <param name="lockedFields">The locked fields.</param>
+        /// <param name="replaceData">if set to <c>true</c> [replace data].</param>
+        /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param>
+        protected override void MergeData(ChannelVideoItem source, ChannelVideoItem target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
+        {
+            ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
+        }
+    }
+}

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

@@ -78,10 +78,13 @@
     <Compile Include="BoxSets\BoxSetXmlParser.cs" />
     <Compile Include="BoxSets\BoxSetXmlParser.cs" />
     <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
     <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" />
     <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
     <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" />
+    <Compile Include="Channels\ChannelMetadataService.cs" />
     <Compile Include="Folders\CollectionFolderImageProvider.cs" />
     <Compile Include="Folders\CollectionFolderImageProvider.cs" />
     <Compile Include="Folders\FolderMetadataService.cs" />
     <Compile Include="Folders\FolderMetadataService.cs" />
     <Compile Include="Folders\ImagesByNameImageProvider.cs" />
     <Compile Include="Folders\ImagesByNameImageProvider.cs" />
+    <Compile Include="GameGenres\AudioChannelItemMetadataService.cs" />
     <Compile Include="GameGenres\GameGenreMetadataService.cs" />
     <Compile Include="GameGenres\GameGenreMetadataService.cs" />
+    <Compile Include="GameGenres\VideoChannelItemMetadataService.cs" />
     <Compile Include="Games\GameMetadataService.cs" />
     <Compile Include="Games\GameMetadataService.cs" />
     <Compile Include="Games\GameSystemMetadataService.cs" />
     <Compile Include="Games\GameSystemMetadataService.cs" />
     <Compile Include="Games\GameSystemXmlParser.cs" />
     <Compile Include="Games\GameSystemXmlParser.cs" />

+ 87 - 0
MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs

@@ -0,0 +1,87 @@
+using MediaBrowser.Common.Net;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Channels
+{
+    public class ChannelItemImageProvider : IDynamicImageProvider, IHasChangeMonitor
+    {
+        private readonly IHttpClient _httpClient;
+        private readonly ILogger _logger;
+
+        public ChannelItemImageProvider(IHttpClient httpClient, ILogger logger)
+        {
+            _httpClient = httpClient;
+            _logger = logger;
+        }
+
+        public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
+        {
+            return new[] { ImageType.Primary };
+        }
+
+        public async Task<DynamicImageResponse> GetImage(IHasImages item, ImageType type, CancellationToken cancellationToken)
+        {
+            var channelItem = (IChannelItem)item;
+
+            var imageResponse = new DynamicImageResponse();
+
+            if (!string.IsNullOrEmpty(channelItem.OriginalImageUrl))
+            {
+                var options = new HttpRequestOptions
+                {
+                    CancellationToken = cancellationToken,
+                    Url = channelItem.OriginalImageUrl
+                };
+
+                var response = await _httpClient.GetResponse(options).ConfigureAwait(false);
+
+                if (response.ContentType.StartsWith("image/", StringComparison.OrdinalIgnoreCase))
+                {
+                    imageResponse.HasImage = true;
+                    imageResponse.Stream = response.Content;
+                    imageResponse.SetFormatFromMimeType(response.ContentType);
+                }
+                else
+                {
+                    _logger.Error("Provider did not return an image content type.");
+                }
+            }
+
+            return imageResponse;
+        }
+
+        public string Name
+        {
+            get { return "Channel Image Provider"; }
+        }
+
+        public bool Supports(IHasImages item)
+        {
+            return item is IChannelItem;
+        }
+
+        public int Order
+        {
+            get { return 0; }
+        }
+
+        public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date)
+        {
+            var channelItem = item as IChannelItem;
+
+            if (channelItem != null)
+            {
+                return !channelItem.HasImage(ImageType.Primary) && !string.IsNullOrWhiteSpace(channelItem.OriginalImageUrl);
+            }
+            return false;
+        }
+    }
+}

+ 257 - 0
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -0,0 +1,257 @@
+using MediaBrowser.Common.Extensions;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Channels;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Controller.Dto;
+using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Library;
+using MediaBrowser.Controller.Providers;
+using MediaBrowser.Model.Channels;
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Logging;
+using MediaBrowser.Model.Querying;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Server.Implementations.Channels
+{
+    public class ChannelManager : IChannelManager
+    {
+        private IChannel[] _channels;
+        private List<Channel> _channelEntities = new List<Channel>();
+
+        private readonly IUserManager _userManager;
+        private readonly IDtoService _dtoService;
+        private readonly ILibraryManager _libraryManager;
+        private readonly ILogger _logger;
+        private readonly IServerConfigurationManager _config;
+        private readonly IFileSystem _fileSystem;
+
+        public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
+        {
+            _userManager = userManager;
+            _dtoService = dtoService;
+            _libraryManager = libraryManager;
+            _logger = logger;
+            _config = config;
+            _fileSystem = fileSystem;
+        }
+
+        public void AddParts(IEnumerable<IChannel> channels)
+        {
+            _channels = channels.ToArray();
+        }
+
+        public Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken)
+        {
+            var user = string.IsNullOrWhiteSpace(query.UserId)
+                ? null
+                : _userManager.GetUserById(new Guid(query.UserId));
+
+            var channels = _channelEntities.OrderBy(i => i.SortName).ToList();
+
+            if (user != null)
+            {
+                channels = channels.Where(i => GetChannelProvider(i).IsEnabledFor(user) && i.IsVisible(user))
+                    .ToList();
+            }
+
+            // Get everything
+            var fields = Enum.GetNames(typeof(ItemFields))
+                    .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+                    .ToList();
+
+            var returnItems = channels.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
+                .ToArray();
+
+            var result = new QueryResult<BaseItemDto>
+            {
+                Items = returnItems,
+                TotalRecordCount = returnItems.Length
+            };
+
+            return Task.FromResult(result);
+        }
+
+        public async Task RefreshChannels(IProgress<double> progress, CancellationToken cancellationToken)
+        {
+            var allChannelsList = _channels.ToList();
+
+            var list = new List<Channel>();
+
+            var numComplete = 0;
+
+            foreach (var channelInfo in allChannelsList)
+            {
+                cancellationToken.ThrowIfCancellationRequested();
+
+                try
+                {
+                    var item = await GetChannel(channelInfo, cancellationToken).ConfigureAwait(false);
+
+                    list.Add(item);
+
+                    _libraryManager.RegisterItem(item);
+                }
+                catch (OperationCanceledException)
+                {
+                    throw;
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error getting channel information for {0}", ex, channelInfo.Name);
+                }
+
+                numComplete++;
+                double percent = numComplete;
+                percent /= allChannelsList.Count;
+
+                progress.Report(100 * percent);
+            }
+
+            _channelEntities = list.ToList();
+            progress.Report(100);
+        }
+
+        private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken)
+        {
+            var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name));
+
+            var fileInfo = new DirectoryInfo(path);
+
+            var isNew = false;
+
+            if (!fileInfo.Exists)
+            {
+                _logger.Debug("Creating directory {0}", path);
+
+                Directory.CreateDirectory(path);
+                fileInfo = new DirectoryInfo(path);
+
+                if (!fileInfo.Exists)
+                {
+                    throw new IOException("Path not created: " + path);
+                }
+
+                isNew = true;
+            }
+
+            var id = GetInternalChannelId(channelInfo.Name);
+
+            var item = _libraryManager.GetItemById(id) as Channel;
+
+            if (item == null)
+            {
+                item = new Channel
+                {
+                    Name = channelInfo.Name,
+                    Id = id,
+                    DateCreated = _fileSystem.GetCreationTimeUtc(fileInfo),
+                    DateModified = _fileSystem.GetLastWriteTimeUtc(fileInfo),
+                    Path = path
+                };
+
+                isNew = true;
+            }
+
+            item.HomePageUrl = channelInfo.HomePageUrl;
+            item.OriginalChannelName = channelInfo.Name;
+
+            if (string.IsNullOrEmpty(item.Name))
+            {
+                item.Name = channelInfo.Name;
+            }
+
+            await item.RefreshMetadata(new MetadataRefreshOptions
+            {
+                ForceSave = isNew
+
+            }, cancellationToken);
+
+            return item;
+        }
+
+        private Guid GetInternalChannelId(string name)
+        {
+            if (string.IsNullOrWhiteSpace(name))
+            {
+                throw new ArgumentNullException("name");
+            }
+
+            return ("Channel " + name).GetMBId(typeof(Channel));
+        }
+
+        public async Task<QueryResult<BaseItemDto>> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken)
+        {
+            var user = string.IsNullOrWhiteSpace(query.UserId)
+                ? null
+                : _userManager.GetUserById(new Guid(query.UserId));
+
+            var id = new Guid(query.ChannelId);
+            var channel = _channelEntities.First(i => i.Id == id);
+            var channelProvider = GetChannelProvider(channel);
+
+            var items = await GetChannelItems(channelProvider, user, query.CategoryId, cancellationToken)
+                        .ConfigureAwait(false);
+
+
+            return await GetReturnItems(items, user, query.StartIndex, query.Limit, cancellationToken).ConfigureAwait(false);
+        }
+
+        private Task<IEnumerable<ChannelItemInfo>> GetChannelItems(IChannel channel, User user, string categoryId, CancellationToken cancellationToken)
+        {
+            // TODO: Put some caching in here
+
+            if (string.IsNullOrWhiteSpace(categoryId))
+            {
+                return channel.GetChannelItems(user, cancellationToken);
+            }
+
+            return channel.GetChannelItems(categoryId, user, cancellationToken);
+        }
+
+        private async Task<QueryResult<BaseItemDto>> GetReturnItems(IEnumerable<ChannelItemInfo> items, User user, int? startIndex, int? limit, CancellationToken cancellationToken)
+        {
+            if (startIndex.HasValue)
+            {
+                items = items.Skip(startIndex.Value);
+            }
+            if (limit.HasValue)
+            {
+                items = items.Take(limit.Value);
+            }
+
+            // Get everything
+            var fields = Enum.GetNames(typeof(ItemFields))
+                    .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true))
+                    .ToList();
+
+            var tasks = items.Select(GetChannelItemEntity);
+
+            var returnItems = await Task.WhenAll(tasks).ConfigureAwait(false);
+
+            var returnItemArray = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user))
+                .ToArray();
+
+            return new QueryResult<BaseItemDto>
+            {
+                Items = returnItemArray,
+                TotalRecordCount = returnItems.Length
+            };
+        }
+
+        private async Task<BaseItem> GetChannelItemEntity(ChannelItemInfo info)
+        {
+            return null;
+        }
+
+        private IChannel GetChannelProvider(Channel channel)
+        {
+            return _channels.First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase));
+        }
+    }
+}

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

@@ -306,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
         private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
         private async Task<LiveTvChannel> GetChannel(ChannelInfo channelInfo, string serviceName, CancellationToken cancellationToken)
         {
         {
-            var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "channels", _fileSystem.GetValidFilename(channelInfo.Name));
+            var path = Path.Combine(_config.ApplicationPaths.ItemsByNamePath, "tvchannels", _fileSystem.GetValidFilename(channelInfo.Name));
 
 
             var fileInfo = new DirectoryInfo(path);
             var fileInfo = new DirectoryInfo(path);
 
 

+ 2 - 0
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -106,6 +106,8 @@
       <Link>Properties\SharedVersion.cs</Link>
       <Link>Properties\SharedVersion.cs</Link>
     </Compile>
     </Compile>
     <Compile Include="BdInfo\BdInfoExaminer.cs" />
     <Compile Include="BdInfo\BdInfoExaminer.cs" />
+    <Compile Include="Channels\ChannelItemImageProvider.cs" />
+    <Compile Include="Channels\ChannelManager.cs" />
     <Compile Include="Collections\CollectionManager.cs" />
     <Compile Include="Collections\CollectionManager.cs" />
     <Compile Include="Collections\CollectionsDynamicFolder.cs" />
     <Compile Include="Collections\CollectionsDynamicFolder.cs" />
     <Compile Include="Configuration\ServerConfigurationManager.cs" />
     <Compile Include="Configuration\ServerConfigurationManager.cs" />

+ 5 - 0
MediaBrowser.ServerApplication/ApplicationHost.cs

@@ -9,6 +9,7 @@ using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Common.Progress;
 using MediaBrowser.Controller;
 using MediaBrowser.Controller;
+using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Dlna;
 using MediaBrowser.Controller.Dlna;
@@ -39,6 +40,7 @@ using MediaBrowser.Model.Updates;
 using MediaBrowser.Providers.Manager;
 using MediaBrowser.Providers.Manager;
 using MediaBrowser.Server.Implementations;
 using MediaBrowser.Server.Implementations;
 using MediaBrowser.Server.Implementations.BdInfo;
 using MediaBrowser.Server.Implementations.BdInfo;
+using MediaBrowser.Server.Implementations.Channels;
 using MediaBrowser.Server.Implementations.Collections;
 using MediaBrowser.Server.Implementations.Collections;
 using MediaBrowser.Server.Implementations.Configuration;
 using MediaBrowser.Server.Implementations.Configuration;
 using MediaBrowser.Server.Implementations.Drawing;
 using MediaBrowser.Server.Implementations.Drawing;
@@ -489,6 +491,9 @@ namespace MediaBrowser.ServerApplication
                 MediaEncoder);
                 MediaEncoder);
             RegisterSingleInstance(EncodingManager);
             RegisterSingleInstance(EncodingManager);
 
 
+            var channelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager);
+            RegisterSingleInstance<IChannelManager>(channelManager);
+
             var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
             var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);
             RegisterSingleInstance<IAppThemeManager>(appThemeManager);
             RegisterSingleInstance<IAppThemeManager>(appThemeManager);
 
 

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

@@ -156,9 +156,7 @@
     <Content Include="dashboard-ui\css\images\icons\audiocd.png">
     <Content Include="dashboard-ui\css\images\icons\audiocd.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\css\images\icons\filter.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
+    <Content Include="dashboard-ui\css\images\icons\filter.png" />
     <Content Include="dashboard-ui\css\images\icons\mute.png">
     <Content Include="dashboard-ui\css\images\icons\mute.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -174,9 +172,6 @@
     <Content Include="dashboard-ui\css\images\icons\previoustrack.png">
     <Content Include="dashboard-ui\css\images\icons\previoustrack.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\css\images\icons\remote.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\css\images\icons\sort.png">
     <Content Include="dashboard-ui\css\images\icons\sort.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
@@ -186,9 +181,6 @@
     <Content Include="dashboard-ui\css\images\icons\subtitles.png">
     <Content Include="dashboard-ui\css\images\icons\subtitles.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>
-    <Content Include="dashboard-ui\css\images\icons\tv.png">
-      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
-    </Content>
     <Content Include="dashboard-ui\css\images\icons\volumedown.png">
     <Content Include="dashboard-ui\css\images\icons\volumedown.png">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     </Content>