diff --git a/MediaBrowser.Api/ChannelService.cs b/MediaBrowser.Api/ChannelService.cs index ed203a0f4..a0795c14d 100644 --- a/MediaBrowser.Api/ChannelService.cs +++ b/MediaBrowser.Api/ChannelService.cs @@ -1,8 +1,12 @@ using MediaBrowser.Controller.Channels; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Querying; using ServiceStack; +using System; +using System.Collections.Generic; +using System.Linq; using System.Threading; namespace MediaBrowser.Api @@ -10,10 +14,25 @@ namespace MediaBrowser.Api [Route("/Channels", "GET", Summary = "Gets available channels")] public class GetChannels : IReturn> { + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] public string UserId { get; set; } + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? StartIndex { get; set; } + /// + /// The maximum number of items to return + /// + /// The limit. + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } } @@ -24,11 +43,51 @@ namespace MediaBrowser.Api public string CategoryId { get; set; } + /// + /// Gets or sets the user id. + /// + /// The user id. + [ApiMember(Name = "UserId", Description = "User Id", IsRequired = false, DataType = "string", ParameterType = "path", Verb = "GET")] public string UserId { get; set; } + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? StartIndex { get; set; } + /// + /// The maximum number of items to return + /// + /// The limit. + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] public int? Limit { get; set; } + + [ApiMember(Name = "SortOrder", Description = "Sort Order - Ascending,Descending", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] + public SortOrder? SortOrder { get; set; } + + [ApiMember(Name = "Filters", Description = "Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsRecentlyAdded, IsResumable, Likes, Dislikes", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string Filters { get; set; } + + [ApiMember(Name = "SortBy", Description = "Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)] + public string SortBy { get; set; } + + /// + /// Gets the filters. + /// + /// IEnumerable{ItemFilter}. + public IEnumerable GetFilters() + { + var val = Filters; + + if (string.IsNullOrEmpty(val)) + { + return new ItemFilter[] { }; + } + + return val.Split(',').Select(v => (ItemFilter)Enum.Parse(typeof(ItemFilter), v, true)); + } } public class ChannelService : BaseApiService @@ -61,7 +120,10 @@ namespace MediaBrowser.Api StartIndex = request.StartIndex, UserId = request.UserId, ChannelId = request.Id, - CategoryId = request.CategoryId + CategoryId = request.CategoryId, + SortOrder = request.SortOrder, + SortBy = (request.SortBy ?? string.Empty).Split(',').Where(i => !string.IsNullOrWhiteSpace(i)).ToArray(), + Filters = request.GetFilters().ToArray() }, CancellationToken.None).Result; diff --git a/MediaBrowser.Controller/Channels/Channel.cs b/MediaBrowser.Controller/Channels/Channel.cs index c9a70f2bc..b894893c6 100644 --- a/MediaBrowser.Controller/Channels/Channel.cs +++ b/MediaBrowser.Controller/Channels/Channel.cs @@ -1,9 +1,21 @@ using MediaBrowser.Controller.Entities; +using System; +using System.Linq; namespace MediaBrowser.Controller.Channels { public class Channel : BaseItem { public string OriginalChannelName { get; set; } + + public override bool IsVisible(User user) + { + if (user.Configuration.BlockedChannels.Contains(Name, StringComparer.OrdinalIgnoreCase)) + { + return false; + } + + return base.IsVisible(user); + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs index 6f3398f52..a0999593f 100644 --- a/MediaBrowser.Controller/Channels/ChannelAudioItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelAudioItem.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Controller.Entities.Audio; +using System.Linq; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Channels { @@ -13,5 +15,18 @@ namespace MediaBrowser.Controller.Channels public ChannelMediaContentType ContentType { get; set; } public string OriginalImageUrl { get; set; } + + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + } + + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs b/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs index 61b38c2b1..67f0ec65f 100644 --- a/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelCategoryItem.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; namespace MediaBrowser.Controller.Channels { @@ -9,5 +10,19 @@ namespace MediaBrowser.Controller.Channels public ChannelItemType ChannelItemType { get; set; } public string OriginalImageUrl { get; set; } + + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + // Don't block. + return false; + } + + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs index 421d3e6f2..104204eb0 100644 --- a/MediaBrowser.Controller/Channels/ChannelItemInfo.cs +++ b/MediaBrowser.Controller/Channels/ChannelItemInfo.cs @@ -18,6 +18,7 @@ namespace MediaBrowser.Controller.Channels public string Overview { get; set; } public List Genres { get; set; } + public List Studios { get; set; } public List People { get; set; } @@ -38,9 +39,15 @@ namespace MediaBrowser.Controller.Channels public DateTime? PremiereDate { get; set; } public int? ProductionYear { get; set; } + public DateTime? DateCreated { get; set; } + + public List MediaSources { get; set; } + public ChannelItemInfo() { + MediaSources = new List(); Genres = new List(); + Studios = new List(); People = new List(); ProviderIds = new Dictionary(StringComparer.OrdinalIgnoreCase); } @@ -70,6 +77,20 @@ namespace MediaBrowser.Controller.Channels Movie = 3, - Episode = 4 + Episode = 4, + + Song = 5 + } + + public class ChannelMediaInfo + { + public string Path { get; set; } + + public Dictionary RequiredHttpHeaders { get; set; } + + public ChannelMediaInfo() + { + RequiredHttpHeaders = new Dictionary(); + } } } diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index 83b85794d..0bf05f965 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -1,4 +1,8 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using System.Globalization; +using System.Linq; namespace MediaBrowser.Controller.Channels { @@ -13,5 +17,41 @@ namespace MediaBrowser.Controller.Channels public ChannelMediaContentType ContentType { get; set; } public string OriginalImageUrl { get; set; } + + public override string GetUserDataKey() + { + if (ContentType == ChannelMediaContentType.Trailer) + { + var key = this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Tvdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? this.GetProviderId(MetadataProviders.Tvcom); + + if (!string.IsNullOrWhiteSpace(key)) + { + key = key + "-trailer"; + + // Make sure different trailers have their own data. + if (RunTimeTicks.HasValue) + { + key += "-" + RunTimeTicks.Value.ToString(CultureInfo.InvariantCulture); + } + + return key; + } + } + + return base.GetUserDataKey(); + } + + protected override bool GetBlockUnratedValue(UserConfiguration config) + { + return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent); + } + + public override bool SupportsLocalMetadata + { + get + { + return false; + } + } } } diff --git a/MediaBrowser.Controller/Channels/IChannel.cs b/MediaBrowser.Controller/Channels/IChannel.cs index 773147a14..0c4be8630 100644 --- a/MediaBrowser.Controller/Channels/IChannel.cs +++ b/MediaBrowser.Controller/Channels/IChannel.cs @@ -17,16 +17,10 @@ namespace MediaBrowser.Controller.Channels string Name { get; } /// - /// Gets the home page URL. + /// Gets the channel information. /// - /// The home page URL. - string HomePageUrl { get; } - - /// - /// Gets the capabilities. - /// - /// ChannelCapabilities. - ChannelCapabilities GetCapabilities(); + /// ChannelInfo. + ChannelInfo GetChannelInfo(); /// /// Determines whether [is enabled for] [the specified user]. @@ -67,9 +61,29 @@ namespace MediaBrowser.Controller.Channels IEnumerable GetSupportedChannelImages(); } - public class ChannelCapabilities + public class ChannelInfo { + /// + /// Gets the home page URL. + /// + /// The home page URL. + public string HomePageUrl { get; set; } + + /// + /// Gets or sets a value indicating whether this instance can search. + /// + /// true if this instance can search; otherwise, false. public bool CanSearch { get; set; } + + public List MediaTypes { get; set; } + + public List ContentTypes { get; set; } + + public ChannelInfo() + { + MediaTypes = new List(); + ContentTypes = new List(); + } } public class ChannelSearchInfo diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 9e18dc9b0..7529f1e0d 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -9,6 +9,7 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Querying; namespace MediaBrowser.Controller.Library { diff --git a/MediaBrowser.Model/Channels/ChannelQuery.cs b/MediaBrowser.Model/Channels/ChannelQuery.cs index 13a87efb6..e09769b00 100644 --- a/MediaBrowser.Model/Channels/ChannelQuery.cs +++ b/MediaBrowser.Model/Channels/ChannelQuery.cs @@ -1,4 +1,6 @@ - +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Querying; + namespace MediaBrowser.Model.Channels { public class ChannelQuery @@ -35,7 +37,7 @@ namespace MediaBrowser.Model.Channels /// /// The category identifier. public string CategoryId { get; set; } - + /// /// Gets or sets the user identifier. /// @@ -53,5 +55,15 @@ namespace MediaBrowser.Model.Channels /// /// The limit. public int? Limit { get; set; } + + public SortOrder? SortOrder { get; set; } + public string[] SortBy { get; set; } + public ItemFilter[] Filters { get; set; } + + public ChannelItemQuery() + { + Filters = new ItemFilter[] { }; + SortBy = new string[] { }; + } } } diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 8bee53c44..f8df19436 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -57,6 +57,7 @@ namespace MediaBrowser.Model.Configuration public bool GroupMoviesIntoBoxSets { get; set; } public string[] BlockedMediaFolders { get; set; } + public string[] BlockedChannels { get; set; } public UnratedItem[] BlockUnratedItems { get; set; } @@ -74,6 +75,7 @@ namespace MediaBrowser.Model.Configuration GroupMoviesIntoBoxSets = true; BlockedMediaFolders = new string[] { }; + BlockedChannels = new string[] { }; BlockUnratedItems = new UnratedItem[] { }; } } @@ -88,6 +90,7 @@ namespace MediaBrowser.Model.Configuration Book, LiveTvChannel, LiveTvProgram, + ChannelContent, Other } } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index ae5bf7716..e028065c3 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -8,6 +8,7 @@ using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Querying; using System; @@ -25,13 +26,14 @@ namespace MediaBrowser.Server.Implementations.Channels private List _channelEntities = new List(); private readonly IUserManager _userManager; + private readonly IUserDataManager _userDataManager; 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) + public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager) { _userManager = userManager; _dtoService = dtoService; @@ -39,6 +41,7 @@ namespace MediaBrowser.Server.Implementations.Channels _logger = logger; _config = config; _fileSystem = fileSystem; + _userDataManager = userDataManager; } public void AddParts(IEnumerable channels) @@ -158,7 +161,9 @@ namespace MediaBrowser.Server.Implementations.Channels isNew = true; } - item.HomePageUrl = channelInfo.HomePageUrl; + var info = channelInfo.GetChannelInfo(); + + item.HomePageUrl = info.HomePageUrl; item.OriginalChannelName = channelInfo.Name; if (string.IsNullOrEmpty(item.Name)) @@ -198,8 +203,7 @@ namespace MediaBrowser.Server.Implementations.Channels var items = await GetChannelItems(channelProvider, user, query.CategoryId, cancellationToken) .ConfigureAwait(false); - - return await GetReturnItems(items, user, query.StartIndex, query.Limit, cancellationToken).ConfigureAwait(false); + return await GetReturnItems(items, user, query, cancellationToken).ConfigureAwait(false); } private async Task> GetChannelItems(IChannel channel, User user, string categoryId, CancellationToken cancellationToken) @@ -217,76 +221,247 @@ namespace MediaBrowser.Server.Implementations.Channels return result.Items; } - private async Task> GetReturnItems(IEnumerable items, User user, int? startIndex, int? limit, CancellationToken cancellationToken) + private async Task> GetReturnItems(IEnumerable items, User user, ChannelItemQuery query, 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 tasks = items.Select(i => GetChannelItemEntity(i, cancellationToken)); - var returnItems = await Task.WhenAll(tasks).ConfigureAwait(false); - returnItems = new BaseItem[] {}; - var returnItemArray = returnItems.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + IEnumerable entities = await Task.WhenAll(tasks).ConfigureAwait(false); + + entities = ApplyFilters(entities, query.Filters, user); + + entities = _libraryManager.Sort(entities, user, query.SortBy, query.SortOrder ?? SortOrder.Ascending); + + var all = entities.ToList(); + var totalCount = all.Count; + + if (query.StartIndex.HasValue) + { + all = all.Skip(query.StartIndex.Value).ToList(); + } + if (query.Limit.HasValue) + { + all = all.Take(query.Limit.Value).ToList(); + } + + await RefreshIfNeeded(all, cancellationToken).ConfigureAwait(false); + + var returnItemArray = all.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) .ToArray(); return new QueryResult { Items = returnItemArray, - TotalRecordCount = returnItems.Length + TotalRecordCount = totalCount }; } - private async Task GetChannelItemEntity(ChannelItemInfo info) + private string GetIdToHash(string externalId) + { + // Increment this as needed to force new downloads + return externalId + "3"; + } + + private async Task GetChannelItemEntity(ChannelItemInfo info, CancellationToken cancellationToken) { BaseItem item; - Guid id; + var isNew = false; if (info.Type == ChannelItemType.Category) { - id = info.Id.GetMBId(typeof(ChannelCategoryItem)); - item = new ChannelCategoryItem(); + id = GetIdToHash(info.Id).GetMBId(typeof(ChannelCategoryItem)); + + item = _libraryManager.GetItemById(id) as ChannelCategoryItem; + + if (item == null) + { + isNew = true; + item = new ChannelCategoryItem(); + } } else if (info.MediaType == ChannelMediaType.Audio) { - id = info.Id.GetMBId(typeof(ChannelCategoryItem)); - item = new ChannelAudioItem(); + id = GetIdToHash(info.Id).GetMBId(typeof(ChannelCategoryItem)); + + item = _libraryManager.GetItemById(id) as ChannelAudioItem; + + if (item == null) + { + isNew = true; + item = new ChannelAudioItem(); + } } else { - id = info.Id.GetMBId(typeof(ChannelVideoItem)); - item = new ChannelVideoItem(); + id = GetIdToHash(info.Id).GetMBId(typeof(ChannelVideoItem)); + + item = _libraryManager.GetItemById(id) as ChannelVideoItem; + + if (item == null) + { + isNew = true; + item = new ChannelVideoItem(); + } } item.Id = id; - item.Name = info.Name; - item.Genres = info.Genres; - item.CommunityRating = info.CommunityRating; - item.OfficialRating = info.OfficialRating; - item.Overview = info.Overview; - item.People = info.People; - item.PremiereDate = info.PremiereDate; - item.ProductionYear = info.ProductionYear; item.RunTimeTicks = info.RunTimeTicks; - item.ProviderIds = info.ProviderIds; + + var mediaSource = info.MediaSources.FirstOrDefault(); + + item.Path = mediaSource == null ? null : mediaSource.Path; + + if (isNew) + { + item.Name = info.Name; + item.Genres = info.Genres; + item.Studios = info.Studios; + item.CommunityRating = info.CommunityRating; + item.OfficialRating = info.OfficialRating; + item.Overview = info.Overview; + item.People = info.People; + item.PremiereDate = info.PremiereDate; + item.ProductionYear = info.ProductionYear; + item.ProviderIds = info.ProviderIds; + + if (info.DateCreated.HasValue) + { + item.DateCreated = info.DateCreated.Value; + } + } + + var channelItem = (IChannelItem)item; + + channelItem.OriginalImageUrl = info.ImageUrl; + channelItem.ExternalId = info.Id; + channelItem.ChannelItemType = info.Type; + + var channelMediaItem = item as IChannelMediaItem; + + if (channelMediaItem != null) + { + channelMediaItem.IsInfiniteStream = info.IsInfiniteStream; + channelMediaItem.ContentType = info.ContentType; + } + + if (isNew) + { + await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); + _libraryManager.RegisterItem(item); + } return item; } + private async Task RefreshIfNeeded(IEnumerable programs, CancellationToken cancellationToken) + { + foreach (var program in programs) + { + await RefreshIfNeeded(program, cancellationToken).ConfigureAwait(false); + } + } + + private async Task RefreshIfNeeded(BaseItem program, CancellationToken cancellationToken) + { + //if (_refreshedPrograms.ContainsKey(program.Id)) + { + //return; + } + + await program.RefreshMetadata(cancellationToken).ConfigureAwait(false); + + //_refreshedPrograms.TryAdd(program.Id, true); + } + internal IChannel GetChannelProvider(Channel channel) { return _channels.First(i => string.Equals(i.Name, channel.OriginalChannelName, StringComparison.OrdinalIgnoreCase)); } + + private IEnumerable ApplyFilters(IEnumerable items, IEnumerable filters, User user) + { + foreach (var filter in filters.OrderByDescending(f => (int)f)) + { + items = ApplyFilter(items, filter, user); + } + + return items; + } + + private IEnumerable ApplyFilter(IEnumerable items, ItemFilter filter, User user) + { + // Avoid implicitly captured closure + var currentUser = user; + + switch (filter) + { + case ItemFilter.IsFavoriteOrLikes: + return items.Where(item => + { + var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + if (userdata == null) + { + return false; + } + + var likes = userdata.Likes ?? false; + var favorite = userdata.IsFavorite; + + return likes || favorite; + }); + + case ItemFilter.Likes: + return items.Where(item => + { + var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.Likes.HasValue && userdata.Likes.Value; + }); + + case ItemFilter.Dislikes: + return items.Where(item => + { + var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.Likes.HasValue && !userdata.Likes.Value; + }); + + case ItemFilter.IsFavorite: + return items.Where(item => + { + var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.IsFavorite; + }); + + case ItemFilter.IsResumable: + return items.Where(item => + { + var userdata = _userDataManager.GetUserData(user.Id, item.GetUserDataKey()); + + return userdata != null && userdata.PlaybackPositionTicks > 0; + }); + + case ItemFilter.IsPlayed: + return items.Where(item => item.IsPlayed(currentUser)); + + case ItemFilter.IsUnplayed: + return items.Where(item => item.IsUnplayed(currentUser)); + + case ItemFilter.IsFolder: + return items.Where(item => item.IsFolder); + + case ItemFilter.IsNotFolder: + return items.Where(item => !item.IsFolder); + } + + return items; + } } } diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index b828dc0de..11d010ca1 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -228,7 +228,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv return; } - await program.RefreshMetadata(CancellationToken.None).ConfigureAwait(false); + await program.RefreshMetadata(cancellationToken).ConfigureAwait(false); _refreshedPrograms.TryAdd(program.Id, true); } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index c8265490f..8e2fa18b9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -76,6 +76,7 @@ "LabelMaxParentalRating": "Maximum allowed parental rating:", "MaxParentalRatingHelp": "Content with a higher rating will be hidden from this user.", "LibraryAccessHelp": "Select the media folders to share with this user. Administrators will be able to edit all folders using the metadata manager.", + "ChannelAccessHelp": "Select the channels to share with this user. Administrators will be able to edit all channels using the metadata manager.", "ButtonDeleteImage": "Delete Image", "LabelSelectUsers": "Select users:", "ButtonUpload": "Upload", @@ -267,7 +268,7 @@ "LabelAutomaticUpdatesTmdbHelp": "If enabled, new images will be downloaded automatically as they're added to TheMovieDB.org. Existing images will not be replaced.", "LabelAutomaticUpdatesTvdbHelp": "If enabled, new images will be downloaded automatically as they're added to TheTVDB.com. Existing images will not be replaced.", "ExtractChapterImagesHelp": "Extracting chapter images will allow clients to display graphical scene selection menus. The process can be slow, cpu-intensive and may require several gigabytes of space. It runs as a nightly scheduled task at 4am, although this is configurable in the scheduled tasks area. It is not recommended to run this task during peak usage hours.", - "LabelMetadataDownloadLanguage": "Preferred language:", + "LabelMetadataDownloadLanguage": "Preferred download language:", "ButtonAutoScroll": "Auto-scroll", "LabelImageSavingConvention": "Image saving convention:", "LabelImageSavingConventionHelp": "Media Browser recognizes images from most major media applications. Choosing your downloading convention is useful if you also use other products.", diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 340b40119..0740feece 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -507,7 +507,7 @@ namespace MediaBrowser.ServerApplication MediaEncoder); RegisterSingleInstance(EncodingManager); - ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager); + ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager); RegisterSingleInstance(ChannelManager); var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 04f88f509..2db493f02 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -658,6 +658,7 @@ namespace MediaBrowser.WebDashboard.Api "contextmenu.css", "mediaplayer.css", "mediaplayer-video.css", + "librarymenu.css", "librarybrowser.css", "detailtable.css", "posteritem.css", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 384d0bf33..03c77c227 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -163,7 +163,9 @@ PreserveNewest - + + PreserveNewest + PreserveNewest @@ -221,6 +223,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index bf7a53711..22c8a293c 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.358 + 3.0.360 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 02c846d05..e8debd820 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.358 + 3.0.360 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index a2434d67a..2cad4cbde 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.358 + 3.0.360 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - +