diff options
55 files changed, 1014 insertions, 141 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs index 7c67f0a73..435bca0b8 100644 --- a/MediaBrowser.Api/ApiEntryPoint.cs +++ b/MediaBrowser.Api/ApiEntryPoint.cs @@ -314,17 +314,16 @@ namespace MediaBrowser.Api /// </summary> /// <param name="deviceId">The device id.</param> /// <param name="deleteFiles">The delete files.</param> - /// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param> /// <returns>Task.</returns> /// <exception cref="ArgumentNullException">deviceId</exception> - internal Task KillTranscodingJobs(string deviceId, Func<string, bool> deleteFiles, bool acquireLock) + internal Task KillTranscodingJobs(string deviceId, Func<string, bool> deleteFiles) { if (string.IsNullOrEmpty(deviceId)) { throw new ArgumentNullException("deviceId"); } - return KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles, acquireLock); + return KillTranscodingJobs(j => string.Equals(deviceId, j.DeviceId, StringComparison.OrdinalIgnoreCase), deleteFiles); } /// <summary> @@ -332,9 +331,8 @@ namespace MediaBrowser.Api /// </summary> /// <param name="killJob">The kill job.</param> /// <param name="deleteFiles">The delete files.</param> - /// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param> /// <returns>Task.</returns> - internal async Task KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles, bool acquireLock) + internal async Task KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles) { var jobs = new List<TranscodingJob>(); @@ -350,10 +348,7 @@ namespace MediaBrowser.Api return; } - if (acquireLock) - { - await TranscodingStartLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); - } + await TranscodingStartLock.WaitAsync(CancellationToken.None).ConfigureAwait(false); try { @@ -364,10 +359,7 @@ namespace MediaBrowser.Api } finally { - if (acquireLock) - { - TranscodingStartLock.Release(); - } + TranscodingStartLock.Release(); } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 2bb706769..fb8168198 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Api.Playback.Hls // If the playlist doesn't already exist, startup ffmpeg try { - await ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase), false).ConfigureAwait(false); + await ApiEntryPoint.Instance.KillTranscodingJobs(j => j.Type == TranscodingJobType.Hls && string.Equals(j.DeviceId, request.DeviceId, StringComparison.OrdinalIgnoreCase), p => !string.Equals(p, playlistPath, StringComparison.OrdinalIgnoreCase)).ConfigureAwait(false); if (currentTranscodingIndex.HasValue) { diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs index 7e32246a2..a8d4c6b86 100644 --- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs +++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Api.Playback.Hls public void Delete(StopEncodingProcess request) { - var task = ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true, true); + var task = ApiEntryPoint.Instance.KillTranscodingJobs(request.DeviceId, path => true); Task.WaitAll(task); } diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index b156b68d1..ff86c8473 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -566,13 +566,13 @@ namespace MediaBrowser.Api.UserLibrary /// </summary> /// <param name="request">The request.</param> /// <returns>System.Object.</returns> - public object Get(GetIntros request) + public async Task<object> Get(GetIntros request) { var user = _userManager.GetUserById(request.UserId); var item = string.IsNullOrEmpty(request.Id) ? user.RootFolder : _libraryManager.GetItemById(request.Id); - var items = _libraryManager.GetIntros(item, user); + var items = await _libraryManager.GetIntros(item, user).ConfigureAwait(false); // Get everything var fields = Enum.GetNames(typeof(ItemFields)) diff --git a/MediaBrowser.Common/Extensions/BaseExtensions.cs b/MediaBrowser.Common/Extensions/BaseExtensions.cs index 452c47159..be2fbffc6 100644 --- a/MediaBrowser.Common/Extensions/BaseExtensions.cs +++ b/MediaBrowser.Common/Extensions/BaseExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; @@ -55,6 +56,28 @@ namespace MediaBrowser.Common.Extensions } /// <summary> + /// Removes the accent. + /// </summary> + /// <param name="text">The text.</param> + /// <returns>System.String.</returns> + public static string RemoveAccent(this string text) + { + var normalizedString = text.Normalize(NormalizationForm.FormD); + var stringBuilder = new StringBuilder(); + + foreach (var c in normalizedString) + { + var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c); + if (unicodeCategory != UnicodeCategory.NonSpacingMark) + { + stringBuilder.Append(c); + } + } + + return stringBuilder.ToString().Normalize(NormalizationForm.FormC); + } + + /// <summary> /// Gets the M d5. /// </summary> /// <param name="str">The STR.</param> diff --git a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs index 10e486e71..5d133c983 100644 --- a/MediaBrowser.Controller/Channels/ChannelVideoItem.cs +++ b/MediaBrowser.Controller/Channels/ChannelVideoItem.cs @@ -1,4 +1,5 @@ using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Dto; @@ -9,7 +10,7 @@ using System.Linq; namespace MediaBrowser.Controller.Channels { - public class ChannelVideoItem : Video, IChannelMediaItem + public class ChannelVideoItem : Video, IChannelMediaItem, IHasLookupInfo<ChannelItemLookupInfo> { public string ExternalId { get; set; } @@ -87,5 +88,14 @@ namespace MediaBrowser.Controller.Channels return list; } + + public ChannelItemLookupInfo GetLookupInfo() + { + var info = GetItemLookupInfo<ChannelItemLookupInfo>(); + + info.ContentType = ContentType; + + return info; + } } } diff --git a/MediaBrowser.Controller/Channels/IChannelManager.cs b/MediaBrowser.Controller/Channels/IChannelManager.cs index 252e2aee5..07fb89132 100644 --- a/MediaBrowser.Controller/Channels/IChannelManager.cs +++ b/MediaBrowser.Controller/Channels/IChannelManager.cs @@ -60,6 +60,14 @@ namespace MediaBrowser.Controller.Channels Task<QueryResult<BaseItemDto>> GetChannels(ChannelQuery query, CancellationToken cancellationToken); /// <summary> + /// Gets all media internal. + /// </summary> + /// <param name="query">The query.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task<QueryResult<BaseItem>>.</returns> + Task<QueryResult<BaseItem>> GetAllMediaInternal(AllChannelMediaQuery query, CancellationToken cancellationToken); + + /// <summary> /// Gets all media. /// </summary> /// <param name="query">The query.</param> diff --git a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs index db8e2b292..e39d98e13 100644 --- a/MediaBrowser.Controller/Channels/IChannelMediaItem.cs +++ b/MediaBrowser.Controller/Channels/IChannelMediaItem.cs @@ -5,8 +5,6 @@ namespace MediaBrowser.Controller.Channels { public interface IChannelMediaItem : IChannelItem { - bool IsInfiniteStream { get; set; } - long? RunTimeTicks { get; set; } string MediaType { get; } diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs index 25d41565a..447328ea1 100644 --- a/MediaBrowser.Controller/Entities/Audio/Audio.cs +++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs @@ -28,6 +28,7 @@ namespace MediaBrowser.Controller.Entities.Audio public string Container { get; set; } public int? TotalBitrate { get; set; } public List<string> Tags { get; set; } + public ExtraType ExtraType { get; set; } public bool IsThemeMedia { get; set; } diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index 3830fa1c1..e75f17f9a 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -41,16 +41,25 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// The supported image extensions /// </summary> - public static readonly string[] SupportedImageExtensions = new[] { ".png", ".jpg", ".jpeg", ".tbn" }; + public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn" }; /// <summary> /// The trailer folder name /// </summary> - public const string TrailerFolderName = "trailers"; - public const string ThemeSongsFolderName = "theme-music"; - public const string ThemeSongFilename = "theme"; - public const string ThemeVideosFolderName = "backdrops"; - public const string XbmcTrailerFileSuffix = "-trailer"; + public static string TrailerFolderName = "trailers"; + public static string ThemeSongsFolderName = "theme-music"; + public static string ThemeSongFilename = "theme"; + public static string ThemeVideosFolderName = "backdrops"; + + public static List<KeyValuePair<string, ExtraType>> ExtraSuffixes = new List<KeyValuePair<string, ExtraType>> + { + new KeyValuePair<string,ExtraType>("-trailer", ExtraType.Trailer), + new KeyValuePair<string,ExtraType>("-deleted", ExtraType.DeletedScene), + new KeyValuePair<string,ExtraType>("-behindthescenes", ExtraType.BehindTheScenes), + new KeyValuePair<string,ExtraType>("-interview", ExtraType.Interview), + new KeyValuePair<string,ExtraType>("-scene", ExtraType.Scene), + new KeyValuePair<string,ExtraType>("-sample", ExtraType.Sample) + }; public List<ItemImageInfo> ImageInfos { get; set; } @@ -167,7 +176,7 @@ namespace MediaBrowser.Controller.Entities { // Local trailer, special feature, theme video, etc. // An item that belongs to another item but is not part of the Parent-Child tree - return !IsFolder && Parent == null; + return !IsFolder && Parent == null && LocationType == LocationType.FileSystem; } } @@ -552,11 +561,24 @@ namespace MediaBrowser.Controller.Entities .Where(i => string.Equals(i.Name, TrailerFolderName, StringComparison.OrdinalIgnoreCase)) .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)) .ToList(); - - // Support plex/xbmc convention + + var extraTypes = new List<ExtraType> { ExtraType.Trailer }; + var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value)) + .Select(i => i.Key) + .ToList(); + files.AddRange(fileSystemChildren.OfType<FileInfo>() - .Where(i => FileSystem.GetFileNameWithoutExtension(i).EndsWith(XbmcTrailerFileSuffix, StringComparison.OrdinalIgnoreCase) && !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase)) - ); + .Where(i => + { + var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i); + + if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase); + })); return LibraryManager.ResolvePaths<Trailer>(files, directoryService, null).Select(video => { @@ -568,12 +590,79 @@ namespace MediaBrowser.Controller.Entities video = dbItem; } + if (video != null) + { + video.ExtraType = ExtraType.Trailer; + } + + return video; + + // Sort them so that the list can be easily compared for changes + }).OrderBy(i => i.Path).ToList(); + } + + protected IEnumerable<Video> LoadSpecialFeatures(List<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) + { + var files = fileSystemChildren.OfType<DirectoryInfo>() + .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase)) + .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)) + .ToList(); + + var extraTypes = new List<ExtraType> { ExtraType.BehindTheScenes, ExtraType.DeletedScene, ExtraType.Interview, ExtraType.Sample, ExtraType.Scene, ExtraType.Clip }; + var suffixes = ExtraSuffixes.Where(i => extraTypes.Contains(i.Value)) + .Select(i => i.Key) + .ToList(); + + files.AddRange(fileSystemChildren.OfType<FileInfo>() + .Where(i => + { + var nameEithoutExtension = FileSystem.GetFileNameWithoutExtension(i); + + if (!suffixes.Any(s => nameEithoutExtension.EndsWith(s, StringComparison.OrdinalIgnoreCase))) + { + return false; + } + + return !string.Equals(Path, i.FullName, StringComparison.OrdinalIgnoreCase); + })); + + return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video => + { + // Try to retrieve it from the db. If we don't find it, use the resolved version + var dbItem = LibraryManager.GetItemById(video.Id) as Video; + + if (dbItem != null) + { + video = dbItem; + } + + if (video != null) + { + SetExtraTypeFromFilename(video); + } + return video; // Sort them so that the list can be easily compared for changes }).OrderBy(i => i.Path).ToList(); } + private void SetExtraTypeFromFilename(Video item) + { + var name = System.IO.Path.GetFileNameWithoutExtension(item.Path) ?? string.Empty; + + foreach (var suffix in ExtraSuffixes) + { + if (name.EndsWith(suffix.Key, StringComparison.OrdinalIgnoreCase)) + { + item.ExtraType = suffix.Value; + return; + } + } + + item.ExtraType = ExtraType.Clip; + } + /// <summary> /// Loads the theme songs. /// </summary> @@ -600,6 +689,11 @@ namespace MediaBrowser.Controller.Entities audio = dbItem; } + if (audio != null) + { + audio.ExtraType = ExtraType.ThemeSong; + } + return audio; // Sort them so that the list can be easily compared for changes @@ -626,6 +720,11 @@ namespace MediaBrowser.Controller.Entities item = dbItem; } + if (item != null) + { + item.ExtraType = ExtraType.ThemeVideo; + } + return item; // Sort them so that the list can be easily compared for changes diff --git a/MediaBrowser.Controller/Entities/Extensions.cs b/MediaBrowser.Controller/Entities/Extensions.cs index 2a64bd3a4..5e792a03a 100644 --- a/MediaBrowser.Controller/Entities/Extensions.cs +++ b/MediaBrowser.Controller/Entities/Extensions.cs @@ -25,16 +25,11 @@ namespace MediaBrowser.Controller.Entities var current = item.RemoteTrailers.FirstOrDefault(i => string.Equals(i.Url, url, StringComparison.OrdinalIgnoreCase)); - if (current != null) - { - current.IsDirectLink = isDirectLink; - } - else + if (current == null) { item.RemoteTrailers.Add(new MediaUrl { - Url = url, - IsDirectLink = isDirectLink + Url = url }); } } diff --git a/MediaBrowser.Controller/Entities/Movies/Movie.cs b/MediaBrowser.Controller/Entities/Movies/Movie.cs index 837f3b9cf..8d2f19d4c 100644 --- a/MediaBrowser.Controller/Entities/Movies/Movie.cs +++ b/MediaBrowser.Controller/Entities/Movies/Movie.cs @@ -125,7 +125,7 @@ namespace MediaBrowser.Controller.Entities.Movies return hasChanges; } - private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, IEnumerable<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) + private async Task<bool> RefreshSpecialFeatures(MetadataRefreshOptions options, List<FileSystemInfo> fileSystemChildren, CancellationToken cancellationToken) { var newItems = LoadSpecialFeatures(fileSystemChildren, options.DirectoryService).ToList(); var newItemIds = newItems.Select(i => i.Id).ToList(); @@ -141,32 +141,6 @@ namespace MediaBrowser.Controller.Entities.Movies return itemsChanged; } - /// <summary> - /// Loads the special features. - /// </summary> - /// <returns>IEnumerable{Video}.</returns> - private IEnumerable<Video> LoadSpecialFeatures(IEnumerable<FileSystemInfo> fileSystemChildren, IDirectoryService directoryService) - { - var files = fileSystemChildren.OfType<DirectoryInfo>() - .Where(i => string.Equals(i.Name, "extras", StringComparison.OrdinalIgnoreCase) || string.Equals(i.Name, "specials", StringComparison.OrdinalIgnoreCase)) - .SelectMany(i => i.EnumerateFiles("*", SearchOption.TopDirectoryOnly)); - - return LibraryManager.ResolvePaths<Video>(files, directoryService, null).Select(video => - { - // Try to retrieve it from the db. If we don't find it, use the resolved version - var dbItem = LibraryManager.GetItemById(video.Id) as Video; - - if (dbItem != null) - { - video = dbItem; - } - - return video; - - // Sort them so that the list can be easily compared for changes - }).OrderBy(i => i.Path).ToList(); - } - protected override bool GetBlockUnratedValue(UserConfiguration config) { return config.BlockUnratedItems.Contains(UnratedItem.Movie); diff --git a/MediaBrowser.Controller/Entities/Video.cs b/MediaBrowser.Controller/Entities/Video.cs index 492a4a02f..59649de7f 100644 --- a/MediaBrowser.Controller/Entities/Video.cs +++ b/MediaBrowser.Controller/Entities/Video.cs @@ -41,6 +41,7 @@ namespace MediaBrowser.Controller.Entities public string Container { get; set; } public int? TotalBitrate { get; set; } public string ShortOverview { get; set; } + public ExtraType ExtraType { get; set; } /// <summary> /// Gets or sets the timestamp. diff --git a/MediaBrowser.Controller/Library/IIntroProvider.cs b/MediaBrowser.Controller/Library/IIntroProvider.cs index a83d3c5eb..611aab387 100644 --- a/MediaBrowser.Controller/Library/IIntroProvider.cs +++ b/MediaBrowser.Controller/Library/IIntroProvider.cs @@ -1,5 +1,6 @@ using MediaBrowser.Controller.Entities; using System.Collections.Generic; +using System.Threading.Tasks; namespace MediaBrowser.Controller.Library { @@ -14,12 +15,18 @@ namespace MediaBrowser.Controller.Library /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>IEnumerable{System.String}.</returns> - IEnumerable<IntroInfo> GetIntros(BaseItem item, User user); + Task<IEnumerable<IntroInfo>> GetIntros(BaseItem item, User user); /// <summary> /// Gets all intro files. /// </summary> /// <returns>IEnumerable{System.String}.</returns> IEnumerable<string> GetAllIntroFiles(); + + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } } } diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs index 8aaa08fdd..951513962 100644 --- a/MediaBrowser.Controller/Library/ILibraryManager.cs +++ b/MediaBrowser.Controller/Library/ILibraryManager.cs @@ -157,7 +157,7 @@ namespace MediaBrowser.Controller.Library /// <param name="item">The item.</param> /// <param name="user">The user.</param> /// <returns>IEnumerable{System.String}.</returns> - IEnumerable<Video> GetIntros(BaseItem item, User user); + Task<IEnumerable<Video>> GetIntros(BaseItem item, User user); /// <summary> /// Gets all intro files. diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 517200bb7..ce9e83009 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -234,6 +234,7 @@ <Compile Include="Providers\DirectoryService.cs" /> <Compile Include="Providers\ICustomMetadataProvider.cs" /> <Compile Include="Providers\IExternalId.cs" /> + <Compile Include="Providers\IExtrasProvider.cs" /> <Compile Include="Providers\IForcedProvider.cs" /> <Compile Include="Providers\IHasChangeMonitor.cs" /> <Compile Include="Entities\IHasMetadata.cs" /> diff --git a/MediaBrowser.Controller/Providers/IExtrasProvider.cs b/MediaBrowser.Controller/Providers/IExtrasProvider.cs new file mode 100644 index 000000000..953bf02a1 --- /dev/null +++ b/MediaBrowser.Controller/Providers/IExtrasProvider.cs @@ -0,0 +1,39 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Entities; + +namespace MediaBrowser.Controller.Providers +{ + public interface IExtrasProvider + { + /// <summary> + /// Gets the name. + /// </summary> + /// <value>The name.</value> + string Name { get; } + + /// <summary> + /// Supportses the specified item. + /// </summary> + /// <param name="item">The item.</param> + /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns> + bool Supports(IHasMetadata item); + } + + public enum ExtraSource + { + Local = 1, + Metadata = 2, + Remote = 3 + } + + public class ExtraInfo + { + public string Path { get; set; } + + public LocationType LocationType { get; set; } + + public bool IsDownloadable { get; set; } + + public ExtraType ExtraType { get; set; } + } +} diff --git a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs index 7e6c6e140..56f285e4d 100644 --- a/MediaBrowser.Controller/Providers/ItemLookupInfo.cs +++ b/MediaBrowser.Controller/Providers/ItemLookupInfo.cs @@ -1,6 +1,7 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; @@ -236,4 +237,9 @@ namespace MediaBrowser.Controller.Providers public int SeasonIndex { get; set; } } + + public class ChannelItemLookupInfo : ItemLookupInfo + { + public ChannelMediaContentType ContentType { get; set; } + } } diff --git a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs index 9f5eade8e..4ddfa5979 100644 --- a/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs +++ b/MediaBrowser.Controller/Resolvers/EntityResolutionHelper.cs @@ -158,9 +158,10 @@ namespace MediaBrowser.Controller.Resolvers // Normalize // Remove whitespace filename = filename.Replace("-", string.Empty); + filename = filename.Replace(".", string.Empty); filename = Regex.Replace(filename, @"\s+", ""); - var prefixes = new[] { "disc", "cd", "disk" }; + var prefixes = new[] { "disc", "cd", "disk", "vol", "volume" }; foreach (var prefix in prefixes) { @@ -210,7 +211,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = fileSystem.GetCreationTimeUtc(childData); + item.DateCreated = DateTime.UtcNow; } item.DateModified = fileSystem.GetLastWriteTimeUtc(childData); @@ -223,7 +224,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = fileSystem.GetCreationTimeUtc(fileData); + item.DateCreated = DateTime.UtcNow; } item.DateModified = fileSystem.GetLastWriteTimeUtc(fileData); } @@ -233,7 +234,7 @@ namespace MediaBrowser.Controller.Resolvers { if (includeCreationTime) { - item.DateCreated = fileSystem.GetCreationTimeUtc(args.FileInfo); + item.DateCreated = DateTime.UtcNow; } item.DateModified = fileSystem.GetLastWriteTimeUtc(args.FileInfo); } diff --git a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj index 32054beb3..89a17055e 100644 --- a/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj +++ b/MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj @@ -374,6 +374,9 @@ <Compile Include="..\MediaBrowser.Model\Entities\EmptyRequestResult.cs"> <Link>Entities\EmptyRequestResult.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Entities\ExtraType.cs"> + <Link>Entities\ExtraType.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Entities\IHasProviderIds.cs"> <Link>Entities\IHasProviderIds.cs</Link> </Compile> diff --git a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj index e9153117b..acd463f59 100644 --- a/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj +++ b/MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj @@ -334,6 +334,9 @@ <Compile Include="..\MediaBrowser.Model\Entities\EmptyRequestResult.cs"> <Link>Entities\EmptyRequestResult.cs</Link> </Compile> + <Compile Include="..\MediaBrowser.Model\Entities\ExtraType.cs"> + <Link>Entities\ExtraType.cs</Link> + </Compile> <Compile Include="..\MediaBrowser.Model\Entities\IHasProviderIds.cs"> <Link>Entities\IHasProviderIds.cs</Link> </Compile> diff --git a/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs b/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs new file mode 100644 index 000000000..35053e3de --- /dev/null +++ b/MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs @@ -0,0 +1,23 @@ + +namespace MediaBrowser.Model.Configuration +{ + public class CinemaModeConfiguration + { + public bool EnableIntrosForMovies { get; set; } + public bool EnableIntrosForEpisodes { get; set; } + public bool EnableIntrosForWatchedContent { get; set; } + public bool EnableIntrosFromUpcomingTrailers { get; set; } + public bool EnableIntrosFromMoviesInLibrary { get; set; } + public bool EnableCustomIntro { get; set; } + public bool EnableIntrosParentalControl { get; set; } + + public CinemaModeConfiguration() + { + EnableIntrosForMovies = true; + EnableCustomIntro = true; + EnableIntrosFromMoviesInLibrary = true; + EnableIntrosFromUpcomingTrailers = true; + EnableIntrosParentalControl = true; + } + } +} diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 4786194e2..9d125779b 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -179,6 +179,8 @@ namespace MediaBrowser.Model.Configuration public bool SaveMetadataHidden { get; set; } + public bool FindInternetTrailers { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. /// </summary> @@ -204,7 +206,8 @@ namespace MediaBrowser.Model.Configuration RealtimeMonitorDelay = 30; - EnableInternetProviders = true; + EnableInternetProviders = true; + FindInternetTrailers = true; PathSubstitutions = new PathSubstitution[] { }; diff --git a/MediaBrowser.Model/Configuration/UserConfiguration.cs b/MediaBrowser.Model/Configuration/UserConfiguration.cs index 3f25ba656..e535d7805 100644 --- a/MediaBrowser.Model/Configuration/UserConfiguration.cs +++ b/MediaBrowser.Model/Configuration/UserConfiguration.cs @@ -82,6 +82,7 @@ namespace MediaBrowser.Model.Configuration public bool SyncConnectName { get; set; } public bool SyncConnectImage { get; set; } + /// <summary> /// Initializes a new instance of the <see cref="UserConfiguration" /> class. /// </summary> diff --git a/MediaBrowser.Model/Entities/ExtraType.cs b/MediaBrowser.Model/Entities/ExtraType.cs new file mode 100644 index 000000000..ab8da58c0 --- /dev/null +++ b/MediaBrowser.Model/Entities/ExtraType.cs @@ -0,0 +1,16 @@ + +namespace MediaBrowser.Model.Entities +{ + public enum ExtraType + { + Clip = 1, + Trailer = 2, + BehindTheScenes = 3, + DeletedScene = 4, + Interview = 5, + Scene = 6, + Sample = 7, + ThemeSong = 8, + ThemeVideo = 9 + } +} diff --git a/MediaBrowser.Model/Entities/MediaUrl.cs b/MediaBrowser.Model/Entities/MediaUrl.cs index 9aa7207ed..24e3b1492 100644 --- a/MediaBrowser.Model/Entities/MediaUrl.cs +++ b/MediaBrowser.Model/Entities/MediaUrl.cs @@ -6,6 +6,5 @@ namespace MediaBrowser.Model.Entities public string Url { get; set; } public string Name { get; set; } public VideoSize? VideoSize { get; set; } - public bool IsDirectLink { get; set; } } } diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj index c4da280e2..105504121 100644 --- a/MediaBrowser.Model/MediaBrowser.Model.csproj +++ b/MediaBrowser.Model/MediaBrowser.Model.csproj @@ -79,11 +79,13 @@ <Compile Include="Collections\CollectionCreationResult.cs" /> <Compile Include="Configuration\ChannelOptions.cs" /> <Compile Include="Configuration\ChapterOptions.cs" /> + <Compile Include="Configuration\CinemaModeConfiguration.cs" /> <Compile Include="Configuration\XbmcMetadataOptions.cs" /> <Compile Include="Configuration\SubtitlePlaybackMode.cs" /> <Compile Include="Connect\UserLinkType.cs" /> <Compile Include="Drawing\ImageOrientation.cs" /> <Compile Include="Dto\StreamOptions.cs" /> + <Compile Include="Entities\ExtraType.cs" /> <Compile Include="FileOrganization\AutoOrganizeOptions.cs" /> <Compile Include="FileOrganization\TvFileOrganizationOptions.cs" /> <Compile Include="Configuration\BaseApplicationConfiguration.cs" /> diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index e17a96a43..266202b96 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs @@ -50,10 +50,6 @@ namespace MediaBrowser.Providers.BoxSets { var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - if (!string.IsNullOrEmpty(tmdbId)) { await EnsureInfo(tmdbId, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); @@ -62,7 +58,11 @@ namespace MediaBrowser.Providers.BoxSets var info = _json.DeserializeFromFile<RootObject>(dataFilePath); var images = (info.images ?? new Images()).posters ?? new List<Poster>(); - + + var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + var result = new RemoteSearchResult { Name = info.name, diff --git a/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs b/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs index ddd112207..0b3f5a814 100644 --- a/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs +++ b/MediaBrowser.Providers/Channels/VideoChannelItemMetadataService.cs @@ -1,15 +1,15 @@ -using System.Collections.Generic; -using MediaBrowser.Common.IO; +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 VideoChannelItemMetadataService : MetadataService<ChannelVideoItem, ItemLookupInfo> + public class VideoChannelItemMetadataService : MetadataService<ChannelVideoItem, ChannelItemLookupInfo> { public VideoChannelItemMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem) : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 46ba62873..8657d568f 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -489,6 +489,8 @@ namespace MediaBrowser.Providers.Manager if (result.HasMetadata) { + NormalizeRemoteResult(result.Item); + MergeData(result.Item, temp, new List<MetadataFields>(), false, false); refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload; @@ -522,6 +524,19 @@ namespace MediaBrowser.Providers.Manager return refreshResult; } + private void NormalizeRemoteResult(TItemType item) + { + if (!ServerConfigurationManager.Configuration.FindInternetTrailers) + { + var hasTrailers = item as IHasTrailers; + + if (hasTrailers != null) + { + hasTrailers.RemoteTrailers.Clear(); + } + } + } + protected virtual void AfterRemoteRefresh(TItemType item) { diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index f5b43e407..3f81ae6cd 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs @@ -1,11 +1,12 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 34e535a85..601901d18 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -251,7 +251,6 @@ namespace MediaBrowser.Providers.Movies hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl { Url = string.Format("http://www.youtube.com/watch?v={0}", i.source), - IsDirectLink = false, Name = i.name, VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs index 9828a20bc..afecf78f5 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs @@ -1,7 +1,9 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Providers; @@ -45,6 +47,13 @@ namespace MediaBrowser.Providers.Movies return !trailer.IsLocalTrailer; } + var channelItem = item as ChannelVideoItem; + + if (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Trailer) + { + return true; + } + // Don't support local trailers return item is Movie || item is MusicVideo; } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index 00a80af83..b43dd4540 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -56,10 +56,6 @@ namespace MediaBrowser.Providers.Movies public async Task<IEnumerable<RemoteSearchResult>> GetMovieSearchResults(ItemLookupInfo searchInfo, CancellationToken cancellationToken) { - var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + "original"; - var tmdbId = searchInfo.GetProviderId(MetadataProviders.Tmdb); if (!string.IsNullOrEmpty(tmdbId)) @@ -72,6 +68,10 @@ namespace MediaBrowser.Providers.Movies var obj = _jsonSerializer.DeserializeFromFile<CompleteMovieData>(dataFilePath); + var tmdbSettings = await GetTmdbSettings(cancellationToken).ConfigureAwait(false); + + var tmdbImageUrl = tmdbSettings.images.base_url + "original"; + var remoteResult = new RemoteSearchResult { Name = obj.title ?? obj.original_title ?? obj.name, diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs index 78ea3e99c..3676e6d61 100644 --- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs @@ -1,6 +1,8 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Providers; using System; using System.Collections.Generic; @@ -9,7 +11,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Movies { - public class MovieDbTrailerProvider : IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder + public class MovieDbTrailerProvider : IRemoteMetadataProvider<Trailer, TrailerInfo>, IHasOrder, IRemoteMetadataProvider<ChannelVideoItem, ChannelItemLookupInfo> { private readonly IHttpClient _httpClient; @@ -28,6 +30,26 @@ namespace MediaBrowser.Providers.Movies return MovieDbProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); } + public Task<MetadataResult<ChannelVideoItem>> GetMetadata(ChannelItemLookupInfo info, CancellationToken cancellationToken) + { + if (info.ContentType != Model.Channels.ChannelMediaContentType.Trailer) + { + return Task.FromResult(new MetadataResult<ChannelVideoItem>()); + } + + return MovieDbProvider.Current.GetItemMetadata<ChannelVideoItem>(info, cancellationToken); + } + + public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ChannelItemLookupInfo searchInfo, CancellationToken cancellationToken) + { + if (searchInfo.ContentType != ChannelMediaContentType.Trailer) + { + return Task.FromResult<IEnumerable<RemoteSearchResult>>(new List<RemoteSearchResult>()); + } + + return MovieDbProvider.Current.GetMovieSearchResults(searchInfo, cancellationToken); + } + public string Name { get { return MovieDbProvider.Current.Name; } diff --git a/MediaBrowser.Providers/Movies/MovieExternalIds.cs b/MediaBrowser.Providers/Movies/MovieExternalIds.cs index e7625d31c..6ed30cdc9 100644 --- a/MediaBrowser.Providers/Movies/MovieExternalIds.cs +++ b/MediaBrowser.Providers/Movies/MovieExternalIds.cs @@ -1,7 +1,9 @@ -using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; namespace MediaBrowser.Providers.Movies @@ -25,6 +27,13 @@ namespace MediaBrowser.Providers.Movies public bool Supports(IHasProviderIds item) { + var channelItem = item as ChannelVideoItem; + + if (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Trailer) + { + return true; + } + return item is Movie || item is Trailer || item is MusicVideo; } } @@ -140,6 +149,13 @@ namespace MediaBrowser.Providers.Movies public bool Supports(IHasProviderIds item) { + var channelItem = item as ChannelVideoItem; + + if (channelItem != null && channelItem.ContentType == ChannelMediaContentType.Trailer) + { + return true; + } + return item is Movie || item is Trailer || item is MusicVideo || item is Series || item is Episode; } } diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs index 08e9a3abf..4e8d7686c 100644 --- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs @@ -1,8 +1,10 @@ using MediaBrowser.Common.Net; +using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Channels; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Providers; @@ -17,7 +19,7 @@ using System.Threading.Tasks; namespace MediaBrowser.Providers.Omdb { public class OmdbItemProvider : IRemoteMetadataProvider<Series, SeriesInfo>, - IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo> + IRemoteMetadataProvider<Movie, MovieInfo>, IRemoteMetadataProvider<Trailer, TrailerInfo>, IRemoteMetadataProvider<ChannelVideoItem, ChannelItemLookupInfo> { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; @@ -45,6 +47,21 @@ namespace MediaBrowser.Providers.Omdb return new List<RemoteSearchResult>(); } + public Task<MetadataResult<ChannelVideoItem>> GetMetadata(ChannelItemLookupInfo info, CancellationToken cancellationToken) + { + if (info.ContentType != ChannelMediaContentType.Trailer) + { + return Task.FromResult(new MetadataResult<ChannelVideoItem>()); + } + + return GetMovieResult<ChannelVideoItem>(info, cancellationToken); + } + + public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ChannelItemLookupInfo searchInfo, CancellationToken cancellationToken) + { + return new List<RemoteSearchResult>(); + } + public string Name { get { return "The Open Movie Database"; } 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" /> diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index a9765889f..358bd6db3 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -571,7 +571,11 @@ namespace MediaBrowser.WebDashboard.Api "edititemmetadata.js", "edititemimages.js", "edititemsubtitles.js", + + "playbackconfiguration.js", + "cinemamodeconfiguration.js", "encodingsettings.js", + "externalplayer.js", "favorites.js", "gamesrecommendedpage.js", @@ -610,7 +614,7 @@ namespace MediaBrowser.WebDashboard.Api "metadataconfigurationpage.js", "metadataimagespage.js", "metadatasubtitles.js", - "metadataxbmc.js", + "metadatakodi.js", "moviegenres.js", "moviecollections.js", "movies.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index bf95a42d4..de8505ffc 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -101,6 +101,9 @@ <Content Include="dashboard-ui\channelslatest.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\cinemamodeconfiguration.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\css\chromecast.css">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -161,7 +164,7 @@ <Content Include="dashboard-ui\css\images\clients\playstore.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\css\images\clients\xbmc.png">
+ <Content Include="dashboard-ui\css\images\clients\kodi.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\css\images\favicon.ico">
@@ -335,7 +338,7 @@ <Content Include="dashboard-ui\librarypathmapping.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\metadataxbmc.html">
+ <Content Include="dashboard-ui\metadatakodi.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\mypreferencesdisplay.html">
@@ -350,6 +353,9 @@ <Content Include="dashboard-ui\notificationlist.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\playbackconfiguration.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\playlistedit.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -608,6 +614,9 @@ <Content Include="dashboard-ui\scripts\chromecast.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\cinemamodeconfiguration.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\dashboardgeneral.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -659,7 +668,7 @@ <Content Include="dashboard-ui\scripts\librarypathmapping.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\scripts\metadataxbmc.js">
+ <Content Include="dashboard-ui\scripts\metadatakodi.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
@@ -674,6 +683,9 @@ <Content Include="dashboard-ui\scripts\notificationlist.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\playbackconfiguration.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\playlistedit.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -800,9 +812,6 @@ <Content Include="dashboard-ui\thirdparty\cast_sender.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="dashboard-ui\thirdparty\jquery-2.0.3.min.js">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="dashboard-ui\thirdparty\jquery-2.1.1.min.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 1b71c1ea7..01b66d7f3 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.434</version> + <version>3.0.435</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.434" /> + <dependency id="MediaBrowser.Common" version="3.0.435" /> <dependency id="NLog" version="3.1.0.0" /> <dependency id="SimpleInjector" version="2.5.2" /> <dependency id="sharpcompress" version="0.10.2" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index c873587e8..1c9c13bed 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.434</version> + <version>3.0.435</version> <title>MediaBrowser.Common</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index a0dc13b7c..a10715f1d 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Model.Signed</id> - <version>3.0.434</version> + <version>3.0.435</version> <title>MediaBrowser.Model - Signed Edition</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 3105089d3..dd7651e09 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.434</version> + <version>3.0.435</version> <title>Media Browser.Server.Core</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Media Browser Server.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.434" /> + <dependency id="MediaBrowser.Common" version="3.0.435" /> </dependencies> </metadata> <files> |
