diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-09-22 17:56:54 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-09-22 17:56:54 -0400 |
| commit | 1afb28b48797ee53442823cfd395e07d219e8ec3 (patch) | |
| tree | 694d2d8a3449a6d8cb46fddeb73ed1f96be634d7 /MediaBrowser.Server.Implementations | |
| parent | ac201a6cdb22ff158aaca1378361b03b2127686d (diff) | |
add cinema mode feature
Diffstat (limited to 'MediaBrowser.Server.Implementations')
12 files changed, 614 insertions, 38 deletions
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs index 1d95dbc1b..8c510afd2 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs @@ -332,7 +332,7 @@ namespace MediaBrowser.Server.Implementations.Channels { return new ITaskTrigger[] { - new IntervalTrigger{ Interval = TimeSpan.FromHours(6)}, + new IntervalTrigger{ Interval = TimeSpan.FromHours(3)}, }; } diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index dfd24a248..12aa670b3 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -62,7 +62,7 @@ namespace MediaBrowser.Server.Implementations.Channels { get { - return TimeSpan.FromHours(12); + return TimeSpan.FromHours(6); } } @@ -663,7 +663,7 @@ namespace MediaBrowser.Server.Implementations.Channels private async Task<IEnumerable<ChannelItemInfo>> GetLatestItems(ISupportsLatestMedia indexable, IChannel channel, string userId, CancellationToken cancellationToken) { - var cacheLength = TimeSpan.FromHours(12); + var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, "channelmanager-latest", null, false); try @@ -720,7 +720,7 @@ namespace MediaBrowser.Server.Implementations.Channels } } - public async Task<QueryResult<BaseItemDto>> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken) + public async Task<QueryResult<BaseItem>> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken) { var user = string.IsNullOrWhiteSpace(query.UserId) ? null @@ -798,19 +798,43 @@ namespace MediaBrowser.Server.Implementations.Channels var internalItems = await Task.WhenAll(itemTasks).ConfigureAwait(false); await RefreshIfNeeded(internalItems, cancellationToken).ConfigureAwait(false); - var returnItemArray = internalItems.Select(i => _dtoService.GetBaseItemDto(i, query.Fields, user)) - .ToArray(); + var returnItemArray = internalItems.ToArray(); - return new QueryResult<BaseItemDto> + return new QueryResult<BaseItem> { TotalRecordCount = totalCount, Items = returnItemArray }; } + + public async Task<QueryResult<BaseItemDto>> GetAllMedia(AllChannelMediaQuery query, CancellationToken cancellationToken) + { + var user = string.IsNullOrWhiteSpace(query.UserId) + ? null + : _userManager.GetUserById(query.UserId); + + var internalResult = await GetAllMediaInternal(query, cancellationToken).ConfigureAwait(false); + + // Get everything + var fields = Enum.GetNames(typeof(ItemFields)) + .Select(i => (ItemFields)Enum.Parse(typeof(ItemFields), i, true)) + .ToList(); + + var returnItems = internalResult.Items.Select(i => _dtoService.GetBaseItemDto(i, fields, user)) + .ToArray(); + + var result = new QueryResult<BaseItemDto> + { + Items = returnItems, + TotalRecordCount = internalResult.TotalRecordCount + }; + + return result; + } private async Task<ChannelItemResult> GetAllItems(IIndexableChannel indexable, IChannel channel, string userId, CancellationToken cancellationToken) { - var cacheLength = TimeSpan.FromHours(12); + var cacheLength = CacheLength; var cachePath = GetChannelDataCachePath(channel, userId, "channelmanager-allitems", null, false); try @@ -1199,7 +1223,6 @@ namespace MediaBrowser.Server.Implementations.Channels item.Genres = info.Genres; item.Studios = info.Studios; item.CommunityRating = info.CommunityRating; - item.OfficialRating = info.OfficialRating; item.Overview = info.Overview; item.IndexNumber = info.IndexNumber; item.ParentIndexNumber = info.ParentIndexNumber; @@ -1207,6 +1230,7 @@ namespace MediaBrowser.Server.Implementations.Channels item.PremiereDate = info.PremiereDate; item.ProductionYear = info.ProductionYear; item.ProviderIds = info.ProviderIds; + item.OfficialRating = info.OfficialRating; item.DateCreated = info.DateCreated.HasValue ? info.DateCreated.Value : diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs new file mode 100644 index 000000000..b067271c5 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs @@ -0,0 +1,136 @@ +using System.Collections.Generic; +using MediaBrowser.Common.Progress; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Logging; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Channels +{ + public class ChannelPostScanTask : ILibraryPostScanTask + { + private readonly IChannelManager _channelManager; + private readonly IUserManager _userManager; + private readonly ILogger _logger; + + public ChannelPostScanTask(IChannelManager channelManager, IUserManager userManager, ILogger logger) + { + _channelManager = channelManager; + _userManager = userManager; + _logger = logger; + } + + public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) + { + var users = _userManager.Users + .Select(i => i.Id.ToString("N")) + .ToList(); + + var numComplete = 0; + + foreach (var user in users) + { + double percentPerUser = 1; + percentPerUser /= users.Count; + var startingPercent = numComplete * percentPerUser * 100; + + var innerProgress = new ActionableProgress<double>(); + innerProgress.RegisterAction(p => progress.Report(startingPercent + (percentPerUser * p))); + + await DownloadContent(user, cancellationToken, innerProgress).ConfigureAwait(false); + + numComplete++; + double percent = numComplete; + percent /= users.Count; + progress.Report(percent * 100); + } + + progress.Report(100); + } + + private async Task DownloadContent(string user, CancellationToken cancellationToken, IProgress<double> progress) + { + var channels = await _channelManager.GetChannelsInternal(new ChannelQuery + { + UserId = user + + }, cancellationToken); + + var numComplete = 0; + + foreach (var channel in channels.Items) + { + try + { + await GetAllItems(user, channel.Id.ToString("N"), null, false, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting channel content", ex); + } + + numComplete++; + double percent = numComplete; + percent /= channels.Items.Length; + progress.Report(percent * 100); + } + + progress.Report(100); + + } + + private async Task GetAllItems(string user, string channelId, string folderId, bool recursive, CancellationToken cancellationToken) + { + var folderItems = new List<string>(); + + var result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery + { + ChannelId = channelId, + UserId = user, + FolderId = folderId + + }, cancellationToken); + + folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N"))); + + var totalRetrieved = result.Items.Length; + var totalCount = result.TotalRecordCount; + + while (totalRetrieved < totalCount) + { + result = await _channelManager.GetChannelItemsInternal(new ChannelItemQuery + { + ChannelId = channelId, + UserId = user, + StartIndex = totalRetrieved, + FolderId = folderId + + }, cancellationToken); + + folderItems.AddRange(result.Items.Where(i => i.IsFolder).Select(i => i.Id.ToString("N"))); + + totalRetrieved += result.Items.Length; + totalCount = result.TotalRecordCount; + } + + if (recursive) + { + foreach (var folder in folderItems) + { + try + { + await GetAllItems(user, channelId, folder, false, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting channel content", ex); + } + } + } + } + } +} diff --git a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs index 374d04f1d..a1b88a65f 100644 --- a/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs +++ b/MediaBrowser.Server.Implementations/Connect/ConnectManager.cs @@ -402,11 +402,11 @@ namespace MediaBrowser.Server.Implementations.Connect } else if (!string.IsNullOrWhiteSpace(query.Name)) { - url = url + "?nameoremail=" + WebUtility.UrlEncode(query.Name); + url = url + "?name=" + WebUtility.UrlEncode(query.Name); } else if (!string.IsNullOrWhiteSpace(query.Email)) { - url = url + "?nameoremail=" + WebUtility.UrlEncode(query.Email); + url = url + "?name=" + WebUtility.UrlEncode(query.Email); } var options = new HttpRequestOptions diff --git a/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs new file mode 100644 index 000000000..b5e449eae --- /dev/null +++ b/MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs @@ -0,0 +1,361 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Common.Security; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Localization; +using MediaBrowser.Model.Channels; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Entities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Server.Implementations.Intros +{ + public class DefaultIntroProvider : IIntroProvider + { + private readonly ISecurityManager _security; + private readonly IChannelManager _channelManager; + private readonly ILocalizationManager _localization; + private readonly IConfigurationManager _serverConfig; + + public DefaultIntroProvider(ISecurityManager security, IChannelManager channelManager, ILocalizationManager localization, IConfigurationManager serverConfig) + { + _security = security; + _channelManager = channelManager; + _localization = localization; + _serverConfig = serverConfig; + } + + public async Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user) + { + var config = GetOptions(); + + if (item is Movie) + { + if (!config.EnableIntrosForMovies) + { + return new List<IntroInfo>(); + } + } + else if (item is Episode) + { + if (!config.EnableIntrosForEpisodes) + { + return new List<IntroInfo>(); + } + } + else + { + return new List<IntroInfo>(); + } + + if (!IsSupporter) + { + return new List<IntroInfo>(); + } + + var ratingLevel = string.IsNullOrWhiteSpace(item.OfficialRating) + ? (int?)null + : _localization.GetRatingLevel(item.OfficialRating); + + var libaryItems = user.RootFolder.GetRecursiveChildren(user, false) + .ToList(); + + var random = new Random(Environment.TickCount + Guid.NewGuid().GetHashCode()); + + var candidates = new List<ItemWithTrailer>(); + + if (config.EnableIntrosFromMoviesInLibrary) + { + var itemsWithTrailers = libaryItems + .Where(i => + { + var hasTrailers = i as IHasTrailers; + + if (hasTrailers != null && hasTrailers.LocalTrailerIds.Count > 0) + { + if (i is Movie) + { + return true; + } + } + return false; + }); + + candidates.AddRange(itemsWithTrailers.Select(i => new ItemWithTrailer + { + Item = i, + Type = ItemWithTrailerType.ItemWithTrailer, + User = user, + WatchingItem = item, + Random = random + })); + } + + if (config.EnableIntrosFromUpcomingTrailers) + { + var channelTrailers = await _channelManager.GetAllMediaInternal(new AllChannelMediaQuery + { + ContentTypes = new[] { ChannelMediaContentType.Trailer }, + UserId = user.Id.ToString("N") + + }, CancellationToken.None); + + candidates.AddRange(channelTrailers.Items.Select(i => new ItemWithTrailer + { + Item = i, + Type = ItemWithTrailerType.ChannelTrailer, + User = user, + WatchingItem = item, + Random = random + })); + + candidates.AddRange(libaryItems.Where(i => i is Trailer).Select(i => new ItemWithTrailer + { + Item = i, + Type = ItemWithTrailerType.LibraryTrailer, + User = user, + WatchingItem = item, + Random = random + })); + } + + var customIntros = config.EnableCustomIntro ? + GetCustomIntros(item) : + new List<IntroInfo>(); + + var trailerLimit = 2; + if (customIntros.Count > 0) + { + trailerLimit--; + } + + // Avoid implicitly captured closure + var currentUser = user; + return candidates.Where(i => + { + if (config.EnableIntrosParentalControl && !FilterByParentalRating(ratingLevel, i.Item)) + { + return false; + } + + if (!config.EnableIntrosForWatchedContent && i.IsPlayed) + { + return false; + } + return true; + }) + .OrderByDescending(i => i.Score) + .ThenBy(i => Guid.NewGuid()) + .ThenByDescending(i => (i.IsPlayed ? 0 : 1)) + .Select(i => i.IntroInfo) + .Take(trailerLimit) + .Concat(customIntros.Take(1)); + } + + private CinemaModeConfiguration GetOptions() + { + return _serverConfig.GetConfiguration<CinemaModeConfiguration>("cinemamode"); + } + + private List<IntroInfo> GetCustomIntros(BaseItem item) + { + return new List<IntroInfo>(); + } + + private bool FilterByParentalRating(int? ratingLevel, BaseItem item) + { + // Only content rated same or lower + if (ratingLevel.HasValue) + { + var level = string.IsNullOrWhiteSpace(item.OfficialRating) + ? (int?)null + : _localization.GetRatingLevel(item.OfficialRating); + + return level.HasValue && level.Value <= ratingLevel.Value; + } + + return true; + } + + internal static int GetSimiliarityScore(BaseItem item1, BaseItem item2, Random random) + { + var points = 0; + + if (!string.IsNullOrEmpty(item1.OfficialRating) && string.Equals(item1.OfficialRating, item2.OfficialRating, StringComparison.OrdinalIgnoreCase)) + { + points += 10; + } + + // Find common genres + points += item1.Genres.Where(i => item2.Genres.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); + + // Find common tags + points += GetTags(item1).Where(i => GetTags(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); + + // Find common keywords + points += GetKeywords(item1).Where(i => GetKeywords(item2).Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 10); + + // Find common studios + points += item1.Studios.Where(i => item2.Studios.Contains(i, StringComparer.OrdinalIgnoreCase)).Sum(i => 5); + + var item2PeopleNames = item2.People.Select(i => i.Name) + .Distinct(StringComparer.OrdinalIgnoreCase) + .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); + + points += item1.People.Where(i => item2PeopleNames.ContainsKey(i.Name)).Sum(i => + { + if (string.Equals(i.Type, PersonType.Director, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Director, StringComparison.OrdinalIgnoreCase)) + { + return 5; + } + if (string.Equals(i.Type, PersonType.Actor, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Actor, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (string.Equals(i.Type, PersonType.Composer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Composer, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (string.Equals(i.Type, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.GuestStar, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (string.Equals(i.Type, PersonType.Writer, StringComparison.OrdinalIgnoreCase) || string.Equals(i.Role, PersonType.Writer, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + + return 1; + }); + + // Add some randomization so that you're not always seeing the same ones for a given movie + points += random.Next(0, 50); + + return points; + } + + private static IEnumerable<string> GetTags(BaseItem item) + { + var hasTags = item as IHasTags; + if (hasTags != null) + { + return hasTags.Tags; + } + + return new List<string>(); + } + + private static IEnumerable<string> GetKeywords(BaseItem item) + { + var hasTags = item as IHasKeywords; + if (hasTags != null) + { + return hasTags.Keywords; + } + + return new List<string>(); + } + + public IEnumerable<string> GetAllIntroFiles() + { + return new List<string>(); + } + + private bool IsSupporter + { + get { return _security.IsMBSupporter; } + } + + public string Name + { + get { return "Default"; } + } + + internal class ItemWithTrailer + { + internal BaseItem Item; + internal ItemWithTrailerType Type; + internal User User; + internal BaseItem WatchingItem; + internal Random Random; + + private bool? _isPlayed; + public bool IsPlayed + { + get + { + if (!_isPlayed.HasValue) + { + _isPlayed = Item.IsPlayed(User); + } + return _isPlayed.Value; + } + } + + private int? _score; + public int Score + { + get + { + if (!_score.HasValue) + { + _score = GetSimiliarityScore(WatchingItem, Item, Random); + } + return _score.Value; + } + } + + public IntroInfo IntroInfo + { + get + { + var id = Item.Id; + + if (Type == ItemWithTrailerType.ItemWithTrailer) + { + var hasTrailers = Item as IHasTrailers; + + if (hasTrailers != null) + { + id = hasTrailers.LocalTrailerIds.FirstOrDefault(); + } + } + return new IntroInfo + { + ItemId = id + }; + } + } + } + + internal enum ItemWithTrailerType + { + LibraryTrailer, + ChannelTrailer, + ItemWithTrailer + } + } + + public class CinemaModeConfigurationFactory : IConfigurationFactory + { + public IEnumerable<ConfigurationStore> GetConfigurations() + { + return new[] + { + new ConfigurationStore + { + ConfigurationType = typeof(CinemaModeConfiguration), + Key = "cinemamode" + } + }; + } + } + +} diff --git a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs index 7b58dd7c4..e902b939b 100644 --- a/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs +++ b/MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs @@ -95,8 +95,7 @@ namespace MediaBrowser.Server.Implementations.Library return true; } - // Don't misidentify xbmc trailers as a movie - if (filename.IndexOf(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) != -1) + if (BaseItem.ExtraSuffixes.Any(i => filename.IndexOf(i.Key, StringComparison.OrdinalIgnoreCase) != -1)) { return true; } diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs index ad5eac033..6283ceb2a 100644 --- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs +++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs @@ -1193,14 +1193,43 @@ namespace MediaBrowser.Server.Implementations.Library /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>IEnumerable{System.String}.</returns> - public IEnumerable<Video> GetIntros(BaseItem item, User user) + public async Task<IEnumerable<Video>> GetIntros(BaseItem item, User user) { - return IntroProviders.SelectMany(i => i.GetIntros(item, user)) + var tasks = IntroProviders + .OrderBy(i => (i.GetType().Name.IndexOf("Default", StringComparison.OrdinalIgnoreCase) == -1 ? 1 : 0)) + .Take(1) + .Select(i => GetIntros(i, item, user)); + + var items = await Task.WhenAll(tasks).ConfigureAwait(false); + + return items + .SelectMany(i => i.ToArray()) .Select(ResolveIntro) .Where(i => i != null); } /// <summary> + /// Gets the intros. + /// </summary> + /// <param name="provider">The provider.</param> + /// <param name="item">The item.</param> + /// <param name="user">The user.</param> + /// <returns>Task<IEnumerable<IntroInfo>>.</returns> + private async Task<IEnumerable<IntroInfo>> GetIntros(IIntroProvider provider, BaseItem item, User user) + { + try + { + return await provider.GetIntros(item, user).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error getting intros", ex); + + return new List<IntroInfo>(); + } + } + + /// <summary> /// Gets all intro files. /// </summary> /// <returns>IEnumerable{System.String}.</returns> @@ -1487,7 +1516,7 @@ namespace MediaBrowser.Server.Implementations.Library var item = GetItemById(id) as UserView; - if (item == null || + if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase)) { Directory.CreateDirectory(path); diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs index b483f7c42..662a9e87c 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/LocalTrailerResolver.cs @@ -2,8 +2,10 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Resolvers; +using MediaBrowser.Model.Entities; using System; using System.IO; +using System.Linq; namespace MediaBrowser.Server.Implementations.Library.Resolvers { @@ -41,9 +43,15 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers } // Support xbmc local trailer convention, but only when looking for local trailers (hence the parent == null check) - if (args.Parent == null && _fileSystem.GetFileNameWithoutExtension(args.Path).EndsWith(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase)) + if (args.Parent == null) { - return base.Resolve(args); + var nameWithoutExtension = _fileSystem.GetFileNameWithoutExtension(args.Path); + var suffix = BaseItem.ExtraSuffixes.First(i => i.Value == ExtraType.Trailer); + + if (nameWithoutExtension.EndsWith(suffix.Key, StringComparison.OrdinalIgnoreCase)) + { + return base.Resolve(args); + } } } diff --git a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs index 215cff22f..51835c1ba 100644 --- a/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs +++ b/MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs @@ -111,8 +111,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies } var filename = Path.GetFileName(args.Path); - // Don't misidentify xbmc trailers as a movie - if (filename.IndexOf(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) != -1) + // Don't misidentify extras or trailers + if (BaseItem.ExtraSuffixes.Any(i => filename.IndexOf(i.Key, StringComparison.OrdinalIgnoreCase) != -1)) { return null; } @@ -229,8 +229,8 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies continue; } - // Don't misidentify xbmc trailers as a movie - if (filename.IndexOf(BaseItem.XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) != -1) + // Don't misidentify extras or trailers as a movie + if (BaseItem.ExtraSuffixes.Any(i => filename.IndexOf(i.Key, StringComparison.OrdinalIgnoreCase) != -1)) { continue; } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index ab4eba16a..c8b4cbb46 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -569,5 +569,6 @@ "MediaInfoStreamTypeVideo": "Video", "MediaInfoStreamTypeSubtitle": "Subtitle", "MediaInfoStreamTypeEmbeddedImage": "Embedded Image", - "MediaInfoRefFrames": "Ref frames" + "MediaInfoRefFrames": "Ref frames", + "TabPlayback": "Playback" } diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index ab7bcf37e..e1b3b5ea1 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -288,7 +288,7 @@ "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.", - "OptionImageSavingCompatible": "Compatible - Media Browser/Xbmc/Plex", + "OptionImageSavingCompatible": "Compatible - Media Browser/Kodi/Plex", "OptionImageSavingStandard": "Standard - MB2", "ButtonSignIn": "Sign In", "TitleSignIn": "Sign In", @@ -883,22 +883,22 @@ "OptionLatestTvRecordings": "Latest recordings", "LabelProtocolInfo": "Protocol info:", "LabelProtocolInfoHelp": "The value that will be used when responding to GetProtocolInfo requests from the device.", - "TabXbmcMetadata": "Xbmc", - "HeaderXbmcMetadataHelp": "Media Browser includes native support for Xbmc Nfo metadata and images. To enable or disable Xbmc metadata, use the Advanced tab to configure options for your media types.", - "LabelXbmcMetadataUser": "Add user watch data to nfo's for:", - "LabelXbmcMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Xbmc.", - "LabelXbmcMetadataDateFormat": "Release date format:", - "LabelXbmcMetadataDateFormatHelp": "All dates within nfo's will be read and written to using this format.", - "LabelXbmcMetadataSaveImagePaths": "Save image paths within nfo files", - "LabelXbmcMetadataSaveImagePathsHelp": "This is recommended if you have image file names that don't conform to Xbmc guidelines.", - "LabelXbmcMetadataEnablePathSubstitution": "Enable path substitution", - "LabelXbmcMetadataEnablePathSubstitutionHelp": "Enables path substitution of image paths using the server's path substitution settings.", - "LabelXbmcMetadataEnablePathSubstitutionHelp2": "See path substitution.", + "TabKodiMetadata": "Kodi", + "HeaderKodiMetadataHelp": "Media Browser includes native support for Kodi Nfo metadata and images. To enable or disable Kodi metadata, use the Advanced tab to configure options for your media types.", + "LabelKodiMetadataUser": "Add user watch data to nfo's for:", + "LabelKodiMetadataUserHelp": "Enable this to keep watch data in sync between Media Browser and Kodi.", + "LabelKodiMetadataDateFormat": "Release date format:", + "LabelKodiMetadataDateFormatHelp": "All dates within nfo's will be read and written to using this format.", + "LabelKodiMetadataSaveImagePaths": "Save image paths within nfo files", + "LabelKodiMetadataSaveImagePathsHelp": "This is recommended if you have image file names that don't conform to Kodi guidelines.", + "LabelKodiMetadataEnablePathSubstitution": "Enable path substitution", + "LabelKodiMetadataEnablePathSubstitutionHelp": "Enables path substitution of image paths using the server's path substitution settings.", + "LabelKodiMetadataEnablePathSubstitutionHelp2": "See path substitution.", "LabelGroupChannelsIntoViews": "Display the following channels directly within my views:", "LabelGroupChannelsIntoViewsHelp": "If enabled, these channels will be displayed directly alongside other views. If disabled, they'll be displayed within a separate Channels view.", "LabelDisplayCollectionsView": "Display a collections view to show movie collections", - "LabelXbmcMetadataEnableExtraThumbs": "Copy extrafanart into extrathumbs", - "LabelXbmcMetadataEnableExtraThumbsHelp": "When downloading images they can be saved into both extrafanart and extrathumbs for maximum Xbmc skin compatibility.", + "LabelKodiMetadataEnableExtraThumbs": "Copy extrafanart into extrathumbs", + "LabelKodiMetadataEnableExtraThumbsHelp": "When downloading images they can be saved into both extrafanart and extrathumbs for maximum Kodi skin compatibility.", "TabServices": "Services", "TabLogs": "Logs", "HeaderServerLogFiles": "Server log files:", @@ -1179,5 +1179,21 @@ "OptionExternallyDownloaded": "External download", "OptionHlsSegmentedSubtitles": "Hls segmented subtitles", "LabelSubtitleFormatHelp": "Example: srt", - "ButtonLearnMore": "Learn more" + "ButtonLearnMore": "Learn more", + "TabPlayback": "Playback", + "HeaderTrailersAndExtras": "Trailers & Extras", + "OptionFindTrailers": "Find trailers from the internet automatically", + "HeaderLanguagePreferences": "Language Preferences", + "TabCinemaMode": "Cinema Mode", + "TitlePlayback": "Playback", + "LabelEnableCinemaModeFor": "Enable cinema mode for:", + "CinemaModeConfigurationHelp": "Cinema mode brings the theater experience straight to your living room with the ability to play trailers and custom intros before the main feature.", + "LabelEnableTheFollowingIntros": "Enable the following types of intros:", + "OptionTrailersFromMyMovies": "Trailers from movies in my library", + "OptionUpcomingMoviesInTheaters": "Trailers from upcoming movies", + "LabelLimitIntrosToUnwatchedContent": "Only use trailers from unwatched content", + "LabelEnableIntroParentalControl": "Enable smart parental control", + "LabelEnableIntroParentalControlHelp": "Intros will only used from content with a parental rating equal to or less than the content being watched.", + "LabelEnableTheFollowingIntrosHelp": "Trailers from existing movies requires setup of local trailers. Theater trailers require installation of the Trailer channel plugin.", + "ButtonThisFeatureRequiresSupporter": "This feature requires an active supporter membership" } diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index d7c06e2f4..dbecd0a92 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -111,6 +111,7 @@ <Compile Include="Channels\ChannelImageProvider.cs" /> <Compile Include="Channels\ChannelItemImageProvider.cs" /> <Compile Include="Channels\ChannelManager.cs" /> + <Compile Include="Channels\ChannelPostScanTask.cs" /> <Compile Include="Channels\RefreshChannelsScheduledTask.cs" /> <Compile Include="Collections\CollectionManager.cs" /> <Compile Include="Collections\CollectionsDynamicFolder.cs" /> @@ -173,6 +174,7 @@ <Compile Include="HttpServer\SocketSharp\WebSocketSharpRequest.cs" /> <Compile Include="HttpServer\SocketSharp\WebSocketSharpResponse.cs" /> <Compile Include="HttpServer\ThrottledStream.cs" /> + <Compile Include="Intros\DefaultIntroProvider.cs" /> <Compile Include="IO\LibraryMonitor.cs" /> <Compile Include="Library\CoreResolutionIgnoreRule.cs" /> <Compile Include="Library\LibraryManager.cs" /> |
