diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs new file mode 100644 index 0000000000..aa8fc9e52a --- /dev/null +++ b/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> + { + 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); + } + } +} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index e19fbb967f..38cf39b54f 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -66,6 +66,7 @@ Properties\SharedVersion.cs + diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs new file mode 100644 index 0000000000..c9a70f2bc2 --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs new file mode 100644 index 0000000000..269a954560 --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 496fbfbf45..f80ad89110 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -1,9 +1,11 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; +using System; using System.Collections.Generic; namespace MediaBrowser.Controller.Channels { - public class ChannelItemInfo + public class ChannelItemInfo : IHasProviderIds { public string Name { get; set; } @@ -23,18 +25,21 @@ namespace MediaBrowser.Controller.Channels public long? RunTimeTicks { get; set; } - public bool IsInfinite { get; set; } + public bool IsInfiniteStream { get; set; } public string ImageUrl { get; set; } public ChannelMediaType MediaType { get; set; } public ChannelMediaContentType ContentType { get; set; } + + public Dictionary ProviderIds { get; set; } public ChannelItemInfo() { Genres = new List(); People = new List(); + ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs new file mode 100644 index 0000000000..baa4c2a17e --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs index ba1bd40832..956eb67e8a 100644 --- a/MediaBrowser.Controller/Channels/IChannel.cs +++ b/MediaBrowser.Controller/Channels/IChannel.cs @@ -25,14 +25,21 @@ namespace MediaBrowser.Controller.Channels /// ChannelCapabilities. ChannelCapabilities GetCapabilities(); + /// + /// Determines whether [is enabled for] [the specified user]. + /// + /// The user. + /// true if [is enabled for] [the specified user]; otherwise, false. + bool IsEnabledFor(User user); + /// /// Searches the specified search term. /// - /// The search term. + /// The search information. /// The user. /// The cancellation token. /// Task{IEnumerable{ChannelItemInfo}}. - Task> Search(string searchTerm, User user, CancellationToken cancellationToken); + Task> Search(ChannelSearchInfo searchInfo, User user, CancellationToken cancellationToken); /// /// Gets the channel items. @@ -56,4 +63,9 @@ namespace MediaBrowser.Controller.Channels { public bool CanSearch { get; set; } } + + public class ChannelSearchInfo + { + public string SearchTerm { get; set; } + } } diff --git a/MediaBrowser.Controller/Channels/IChannelItem.cs b/MediaBrowser.Controller/Channels/IChannelItem.cs new file mode 100644 index 0000000000..ffef532a9c --- /dev/null +++ b/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; } + } +} diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 561ab555bd..b34e77415f 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/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.Linq; -using System.Text; +using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Channels { public interface IChannelManager { + /// + /// Adds the parts. + /// + /// The channels. + void AddParts(IEnumerable channels); + + /// + /// Gets the channels. + /// + /// The query. + /// The cancellation token. + /// Task{QueryResult{BaseItemDto}}. + Task> GetChannels(ChannelQuery query, CancellationToken cancellationToken); + + /// + /// Gets the channel items. + /// + /// The query. + /// The cancellation token. + /// Task{QueryResult{BaseItemDto}}. + Task> GetChannelItems(ChannelItemQuery query, CancellationToken cancellationToken); } } diff --git a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs index a3a6155160..4ccba11063 100644 --- a/MediaBrowser.Controller/Dlna/DeviceIdentification.cs +++ b/MediaBrowser.Controller/Dlna/DeviceIdentification.cs @@ -25,6 +25,21 @@ namespace MediaBrowser.Controller.Dlna /// The name of the model. public string ModelName { get; set; } /// + /// Gets or sets the model description. + /// + /// The model description. + public string ModelDescription { get; set; } + /// + /// Gets or sets the device description. + /// + /// The device description. + public string DeviceDescription { get; set; } + /// + /// Gets or sets the model URL. + /// + /// The model URL. + public string ModelUrl { get; set; } + /// /// Gets or sets the manufacturer. /// /// diff --git a/MediaBrowser.Controller/Dlna/DeviceProfile.cs b/MediaBrowser.Controller/Dlna/DeviceProfile.cs index 119cfffd74..ca5929d135 100644 --- a/MediaBrowser.Controller/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Controller/Dlna/DeviceProfile.cs @@ -33,6 +33,28 @@ namespace MediaBrowser.Controller.Dlna /// The identification. 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; } + /// + /// Controls the content of the X_DLNADOC element in the urn:schemas-dlna-org:device-1-0 namespace. + /// + public string XDlnaDoc { get; set; } + /// + /// Controls the content of the X_DLNACAP element in the urn:schemas-dlna-org:device-1-0 namespace. + /// + public string XDlnaCap { get; set; } + /// + /// Controls the content of the aggregationFlags element in the urn:schemas-sonycom:av. + /// + public string SonyAggregationFlags { get; set; } + + public string ProtocolInfo { get; set; } + public DeviceProfile() { DirectPlayProfiles = new DirectPlayProfile[] { }; diff --git a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs index 8c35b52a81..68e25e2f7b 100644 --- a/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs +++ b/MediaBrowser.Controller/Dlna/DirectPlayProfile.cs @@ -53,6 +53,7 @@ namespace MediaBrowser.Controller.Dlna } } + public string OrgPn { get; set; } public string MimeType { get; set; } public DlnaProfileType Type { get; set; } diff --git a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs index abc8868fb9..3b0a513d13 100644 --- a/MediaBrowser.Controller/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Controller/Dlna/TranscodingProfile.cs @@ -1,4 +1,5 @@ - +using System.Collections.Generic; + namespace MediaBrowser.Controller.Dlna { public class TranscodingProfile @@ -9,8 +10,28 @@ namespace MediaBrowser.Controller.Dlna public string MimeType { get; set; } + public string OrgPn { get; set; } + public string VideoCodec { get; set; } public string AudioCodec { get; set; } + + public List Settings { get; set; } + + public TranscodingProfile() + { + Settings = new List(); + } + } + + public class TranscodingSetting + { + public TranscodingSettingType Name { get; set; } + public string Value { get; set; } + } + + public enum TranscodingSettingType + { + Profile } } diff --git a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs index 3e6832123e..5b78b67898 100644 --- a/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs +++ b/MediaBrowser.Controller/LiveTv/LiveTvChannel.cs @@ -107,7 +107,7 @@ namespace MediaBrowser.Controller.LiveTv public override string GetClientTypeName() { - return "Channel"; + return "TvChannel"; } public IEnumerable GetTaggedItems(IEnumerable inputItems) diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 2dc444ea93..87fdc66f93 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -71,6 +71,10 @@ + + + + diff --git a/MediaBrowser.Dlna/DlnaManager.cs b/MediaBrowser.Dlna/DlnaManager.cs index b0c1e17498..e8c1b95794 100644 --- a/MediaBrowser.Dlna/DlnaManager.cs +++ b/MediaBrowser.Dlna/DlnaManager.cs @@ -26,8 +26,6 @@ namespace MediaBrowser.Dlna { var list = new List(); - #region Samsung - list.Add(new DeviceProfile { Name = "Samsung TV (B Series)", @@ -79,8 +77,6 @@ namespace MediaBrowser.Dlna Type = DlnaProfileType.Video } } - - }); list.Add(new DeviceProfile @@ -187,10 +183,6 @@ namespace MediaBrowser.Dlna } }); - #endregion - - #region Xbox - list.Add(new DeviceProfile { Name = "Xbox 360", @@ -272,10 +264,6 @@ namespace MediaBrowser.Dlna } }); - #endregion - - #region Sony - list.Add(new DeviceProfile { Name = "Sony Bravia (2012)", @@ -369,10 +357,6 @@ namespace MediaBrowser.Dlna } }); - #endregion - - #region Panasonic - list.Add(new DeviceProfile { //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 list.Add(new DeviceProfile { @@ -625,37 +607,55 @@ namespace MediaBrowser.Dlna 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)) return false; } - if (!string.IsNullOrEmpty(profileInfo.ModelNumber)) - { - if (!Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) - return false; - } - - if (!string.IsNullOrEmpty(profileInfo.ModelName)) - { - if (!Regex.IsMatch(deviceInfo.ModelName, profileInfo.ModelName)) - return false; - } - - if (!string.IsNullOrEmpty(profileInfo.Manufacturer)) + if (!string.IsNullOrWhiteSpace(profileInfo.Manufacturer)) { if (!Regex.IsMatch(deviceInfo.Manufacturer, profileInfo.Manufacturer)) return false; } - if (!string.IsNullOrEmpty(profileInfo.ManufacturerUrl)) + if (!string.IsNullOrWhiteSpace(profileInfo.ManufacturerUrl)) { if (!Regex.IsMatch(deviceInfo.ManufacturerUrl, profileInfo.ManufacturerUrl)) return false; } - if (!string.IsNullOrEmpty(profileInfo.SerialNumber)) + 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)) + return false; + } + + if (!string.IsNullOrWhiteSpace(profileInfo.ModelNumber)) + { + if (!Regex.IsMatch(deviceInfo.ModelNumber, profileInfo.ModelNumber)) + return false; + } + + if (!string.IsNullOrWhiteSpace(profileInfo.ModelUrl)) + { + if (!Regex.IsMatch(deviceInfo.ModelUrl, profileInfo.ModelUrl)) + return false; + } + + if (!string.IsNullOrWhiteSpace(profileInfo.SerialNumber)) { if (!Regex.IsMatch(deviceInfo.SerialNumber, profileInfo.SerialNumber)) return false; diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index c8df3931d9..4a96c580c8 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -74,6 +74,9 @@ ApiClient\ServerEventArgs.cs + + Channels\ChannelQuery.cs + Configuration\AutoOrganize.cs diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index 6e8f230890..958a4a7260 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -61,6 +61,9 @@ ApiClient\ServerEventArgs.cs + + Channels\ChannelQuery.cs + Configuration\AutoOrganize.cs diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs new file mode 100644 index 0000000000..13a87efb61 --- /dev/null +++ b/MediaBrowser.Model/Channels/ChannelQuery.cs @@ -0,0 +1,57 @@ + +namespace MediaBrowser.Model.Channels +{ + public class ChannelQuery + { + /// + /// Gets or sets the user identifier. + /// + /// The user identifier. + public string UserId { get; set; } + + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + public int? Limit { get; set; } + } + + public class ChannelItemQuery + { + /// + /// Gets or sets the channel identifier. + /// + /// The channel identifier. + public string ChannelId { get; set; } + + /// + /// Gets or sets the category identifier. + /// + /// The category identifier. + public string CategoryId { get; set; } + + /// + /// Gets or sets the user identifier. + /// + /// The user identifier. + public string UserId { get; set; } + + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + public int? Limit { get; set; } + } +} diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index 45172da43b..cce3e74cc8 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -58,6 +58,7 @@ + diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs new file mode 100644 index 0000000000..3b87dcdd4d --- /dev/null +++ b/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 + { + public ChannelMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem) + : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem) + { + } + + /// + /// Merges the specified source. + /// + /// The source. + /// The target. + /// The locked fields. + /// if set to true [replace data]. + /// if set to true [merge metadata settings]. + protected override void MergeData(Channel source, Channel target, List lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + } +} diff --git a/MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs b/MediaBrowser.Providers/GameGenres/AudioChannelItemMetadataService.cs new file mode 100644 index 0000000000..c8e496b710 --- /dev/null +++ b/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 + { + public AudioChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem) + : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem) + { + } + + /// + /// Merges the specified source. + /// + /// The source. + /// The target. + /// The locked fields. + /// if set to true [replace data]. + /// if set to true [merge metadata settings]. + protected override void MergeData(ChannelAudioItem source, ChannelAudioItem target, List lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + } +} diff --git a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs index 0eaed59c78..f5659f2d9a 100644 --- a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs +++ b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs @@ -7,8 +7,6 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; namespace MediaBrowser.Providers.GameGenres { diff --git a/MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs b/MediaBrowser.Providers/GameGenres/VideoChannelItemMetadataService.cs new file mode 100644 index 0000000000..9dd37c9595 --- /dev/null +++ b/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 + { + public VideoChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem) + : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem) + { + } + + /// + /// Merges the specified source. + /// + /// The source. + /// The target. + /// The locked fields. + /// if set to true [replace data]. + /// if set to true [merge metadata settings]. + protected override void MergeData(ChannelVideoItem source, ChannelVideoItem target, List lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + } +} diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 395e66529b..f347e72290 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -78,10 +78,13 @@ + + + diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs b/MediaBrowser.Server.Implementations/Channels/ChannelItemImageProvider.cs new file mode 100644 index 0000000000..23504aaaa4 --- /dev/null +++ b/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 GetSupportedImages(IHasImages item) + { + return new[] { ImageType.Primary }; + } + + public async Task 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; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs new file mode 100644 index 0000000000..630c7373e4 --- /dev/null +++ b/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 _channelEntities = new List(); + + 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 channels) + { + _channels = channels.ToArray(); + } + + public Task> 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 + { + Items = returnItems, + TotalRecordCount = returnItems.Length + }; + + return Task.FromResult(result); + } + + public async Task RefreshChannels(IProgress progress, CancellationToken cancellationToken) + { + var allChannelsList = _channels.ToList(); + + var list = new List(); + + 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 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> 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> 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> GetReturnItems(IEnumerable 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 + { + Items = returnItemArray, + TotalRecordCount = returnItems.Length + }; + } + + private async Task GetChannelItemEntity(ChannelItemInfo info) + { + return null; + } + + private IChannel GetChannelProvider(Channel channel) + { + return _channels.First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase)); + } + } +} diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index e4446895f9..6a4c3930a2 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -306,7 +306,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv private async Task 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); diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 8715651331..495288e4f4 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -106,6 +106,8 @@ Properties\SharedVersion.cs + + diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 43aeb0ab0d..00fd63d934 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -9,6 +9,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -39,6 +40,7 @@ using MediaBrowser.Model.Updates; using MediaBrowser.Providers.Manager; using MediaBrowser.Server.Implementations; using MediaBrowser.Server.Implementations.BdInfo; +using MediaBrowser.Server.Implementations.Channels; using MediaBrowser.Server.Implementations.Collections; using MediaBrowser.Server.Implementations.Configuration; using MediaBrowser.Server.Implementations.Drawing; @@ -489,6 +491,9 @@ namespace MediaBrowser.ServerApplication MediaEncoder); RegisterSingleInstance(EncodingManager); + var channelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager); + RegisterSingleInstance(channelManager); + var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); RegisterSingleInstance(appThemeManager); diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 15f1609423..d34bc574ad 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -156,9 +156,7 @@ PreserveNewest - - PreserveNewest - + PreserveNewest @@ -174,9 +172,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -186,9 +181,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest