diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-06-09 15:16:14 -0400 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-06-09 15:16:14 -0400 |
| commit | 945e84327087c9e81371c7b4f940a19a0c083586 (patch) | |
| tree | 0c4f4c2d4c048c3baf4acfe45a7caf8ff9fe931b | |
| parent | ba336372512e6366517a67e8b12677333266d032 (diff) | |
add new chapter provider feature
29 files changed, 534 insertions, 74 deletions
diff --git a/MediaBrowser.Api/Library/ChapterService.cs b/MediaBrowser.Api/Library/ChapterService.cs new file mode 100644 index 0000000000..72ffa3fca6 --- /dev/null +++ b/MediaBrowser.Api/Library/ChapterService.cs @@ -0,0 +1,28 @@ +using MediaBrowser.Controller.Chapters; +using ServiceStack; +using System.Linq; + +namespace MediaBrowser.Api.Library +{ + [Route("/Providers/Chapters", "GET")] + public class GetChapterProviders : IReturnVoid + { + } + + public class ChapterService : BaseApiService + { + private readonly IChapterManager _chapterManager; + + public ChapterService(IChapterManager chapterManager) + { + _chapterManager = chapterManager; + } + + public object Get(GetChapterProviders request) + { + var result = _chapterManager.GetProviders().ToList(); + + return ToOptimizedResult(result); + } + } +} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 92bbb61300..1e9ff9199e 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -68,6 +68,7 @@ <Compile Include="ChannelService.cs" /> <Compile Include="Dlna\DlnaServerService.cs" /> <Compile Include="Dlna\DlnaService.cs" /> + <Compile Include="Library\ChapterService.cs" /> <Compile Include="Library\SubtitleService.cs" /> <Compile Include="Movies\CollectionService.cs" /> <Compile Include="Music\AlbumsService.cs" /> diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 6362f1084c..c2b393fc64 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -363,11 +363,11 @@ namespace MediaBrowser.Api.Playback switch (qualitySetting) { case EncodingQuality.HighSpeed: - crf = "16"; + crf = "12"; profileScore = 2; break; case EncodingQuality.HighQuality: - crf = "10"; + crf = "8"; profileScore = 1; break; case EncodingQuality.MaxQuality: diff --git a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs index 28a3653541..96bbd6dffb 100644 --- a/MediaBrowser.Api/UserLibrary/UserLibraryService.cs +++ b/MediaBrowser.Api/UserLibrary/UserLibraryService.cs @@ -561,7 +561,7 @@ namespace MediaBrowser.Api.UserLibrary return dtos.ToList(); } - throw new ArgumentException("The item does not support special features"); + return new List<BaseItemDto>(); } /// <summary> diff --git a/MediaBrowser.Common.Implementations/Security/UsageReporter.cs b/MediaBrowser.Common.Implementations/Security/UsageReporter.cs index 32e3a90dee..6982d49fde 100644 --- a/MediaBrowser.Common.Implementations/Security/UsageReporter.cs +++ b/MediaBrowser.Common.Implementations/Security/UsageReporter.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.Net; using System; using System.Collections.Generic; +using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -25,13 +26,16 @@ namespace MediaBrowser.Common.Implementations.Security var mac = _networkManager.GetMacAddress(); + var plugins = string.Join("|", _applicationHost.Plugins.Select(i => i.Name).ToArray()); + var data = new Dictionary<string, string> { { "feature", _applicationHost.Name }, { "mac", mac }, { "ver", _applicationHost.ApplicationVersion.ToString() }, { "platform", Environment.OSVersion.VersionString }, - { "isservice", _applicationHost.IsRunningAsService.ToString().ToLower()} + { "isservice", _applicationHost.IsRunningAsService.ToString().ToLower()}, + { "plugins", plugins} }; return _httpClient.Post(Constants.Constants.MbAdminUrl + "service/registration/ping", data, cancellationToken); diff --git a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs b/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs index 9a53d68ea8..982dc35bbc 100644 --- a/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs +++ b/MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs @@ -21,6 +21,8 @@ namespace MediaBrowser.Controller.Chapters public long? RuntimeTicks { get; set; } public Dictionary<string, string> ProviderIds { get; set; } + public bool SearchAllProviders { get; set; } + public ChapterSearchRequest() { ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); diff --git a/MediaBrowser.Controller/Chapters/IChapterManager.cs b/MediaBrowser.Controller/Chapters/IChapterManager.cs new file mode 100644 index 0000000000..df230bf7e3 --- /dev/null +++ b/MediaBrowser.Controller/Chapters/IChapterManager.cs @@ -0,0 +1,57 @@ +using MediaBrowser.Controller.Entities; +using MediaBrowser.Model.Chapters; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Controller.Chapters +{ + /// <summary> + /// Interface IChapterManager + /// </summary> + public interface IChapterManager + { + /// <summary> + /// Adds the parts. + /// </summary> + /// <param name="chapterProviders">The chapter providers.</param> + void AddParts(IEnumerable<IChapterProvider> chapterProviders); + + /// <summary> + /// Searches the specified video. + /// </summary> + /// <param name="video">The video.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns> + Task<IEnumerable<RemoteChapterResult>> Search(Video video, CancellationToken cancellationToken); + + /// <summary> + /// Searches the specified request. + /// </summary> + /// <param name="request">The request.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{IEnumerable{RemoteChapterResult}}.</returns> + Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken); + + /// <summary> + /// Gets the chapters. + /// </summary> + /// <param name="id">The identifier.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{ChapterResponse}.</returns> + Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken); + + /// <summary> + /// Gets the providers. + /// </summary> + /// <param name="itemId">The item identifier.</param> + /// <returns>IEnumerable{ChapterProviderInfo}.</returns> + IEnumerable<ChapterProviderInfo> GetProviders(string itemId); + + /// <summary> + /// Gets the providers. + /// </summary> + /// <returns>IEnumerable{ChapterProviderInfo}.</returns> + IEnumerable<ChapterProviderInfo> GetProviders(); + } +} diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 09bf4b470d..05e8ba2fb2 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -91,6 +91,7 @@ <Compile Include="Channels\IRequiresMediaInfoCallback.cs" /> <Compile Include="Channels\ISearchableChannel.cs" /> <Compile Include="Chapters\ChapterSearchRequest.cs" /> + <Compile Include="Chapters\IChapterManager.cs" /> <Compile Include="Chapters\IChapterProvider.cs" /> <Compile Include="Chapters\ChapterResponse.cs" /> <Compile Include="Collections\CollectionCreationOptions.cs" /> diff --git a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs index 9aed4d9212..3ce6ac46b9 100644 --- a/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs +++ b/MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs @@ -12,7 +12,14 @@ namespace MediaBrowser.Controller.Providers public interface ICustomMetadataProvider<TItemType> : IMetadataProvider<TItemType>, ICustomMetadataProvider where TItemType : IHasMetadata { - Task<ItemUpdateType> FetchAsync(TItemType item, IDirectoryService directoryService, CancellationToken cancellationToken); + /// <summary> + /// Fetches the asynchronous. + /// </summary> + /// <param name="item">The item.</param> + /// <param name="options">The options.</param> + /// <param name="cancellationToken">The cancellation token.</param> + /// <returns>Task{ItemUpdateType}.</returns> + Task<ItemUpdateType> FetchAsync(TItemType item, MetadataRefreshOptions options, CancellationToken cancellationToken); } public interface IPreRefreshProvider : ICustomMetadataProvider diff --git a/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs b/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs index f2674c8424..52b6c1bcdf 100644 --- a/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs +++ b/MediaBrowser.Model/Chapters/RemoteChapterInfo.cs @@ -15,4 +15,10 @@ namespace MediaBrowser.Model.Chapters /// <value>The name.</value> public string Name { get; set; } } + + public class ChapterProviderInfo + { + public string Name { get; set; } + public string Id { get; set; } + } } diff --git a/MediaBrowser.Model/Chapters/RemoteChapterResult.cs b/MediaBrowser.Model/Chapters/RemoteChapterResult.cs index 3f627c8df3..425c3ded82 100644 --- a/MediaBrowser.Model/Chapters/RemoteChapterResult.cs +++ b/MediaBrowser.Model/Chapters/RemoteChapterResult.cs @@ -22,6 +22,12 @@ namespace MediaBrowser.Model.Chapters public string Name { get; set; } /// <summary> + /// Gets or sets the name of the provider. + /// </summary> + /// <value>The name of the provider.</value> + public string ProviderName { get; set; } + + /// <summary> /// Gets or sets the community rating. /// </summary> /// <value>The community rating.</value> diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index 34707ecd7e..bbe862238d 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -193,10 +193,6 @@ namespace MediaBrowser.Model.Configuration public bool AllowVideoUpscaling { get; set; } - public bool EnableMovieChapterImageExtraction { get; set; } - public bool EnableEpisodeChapterImageExtraction { get; set; } - public bool EnableOtherVideoChapterImageExtraction { get; set; } - public MetadataOptions[] MetadataOptions { get; set; } public bool EnableDebugEncodingLogging { get; set; } @@ -227,6 +223,7 @@ namespace MediaBrowser.Model.Configuration public string[] ManualLoginClients { get; set; } public ChannelOptions ChannelOptions { get; set; } + public ChapterOptions ChapterOptions { get; set; } /// <summary> /// Initializes a new instance of the <see cref="ServerConfiguration" /> class. @@ -241,9 +238,6 @@ namespace MediaBrowser.Model.Configuration EnableHttpLevelLogging = true; EnableDashboardResponseCaching = true; - EnableMovieChapterImageExtraction = true; - EnableEpisodeChapterImageExtraction = false; - EnableOtherVideoChapterImageExtraction = false; EnableAutomaticRestart = true; EnablePeoplePrefixSubFolders = true; @@ -297,6 +291,7 @@ namespace MediaBrowser.Model.Configuration SubtitleOptions = new SubtitleOptions(); ChannelOptions = new ChannelOptions(); + ChapterOptions = new ChapterOptions(); } } @@ -315,4 +310,29 @@ namespace MediaBrowser.Model.Configuration MaxDownloadAge = 30; } } + + public class ChapterOptions + { + public bool EnableMovieChapterImageExtraction { get; set; } + public bool EnableEpisodeChapterImageExtraction { get; set; } + public bool EnableOtherVideoChapterImageExtraction { get; set; } + + public bool DownloadMovieChapters { get; set; } + public bool DownloadEpisodeChapters { get; set; } + + public string[] FetcherOrder { get; set; } + public string[] DisabledFetchers { get; set; } + + public ChapterOptions() + { + EnableMovieChapterImageExtraction = true; + EnableEpisodeChapterImageExtraction = false; + EnableOtherVideoChapterImageExtraction = false; + + DownloadMovieChapters = true; + + DisabledFetchers = new string[] { }; + FetcherOrder = new string[] { }; + } + } } diff --git a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs index 2f244b4e39..a48006755e 100644 --- a/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs +++ b/MediaBrowser.Model/LiveTv/ChannelInfoDto.cs @@ -32,6 +32,10 @@ namespace MediaBrowser.Model.LiveTv /// <value>The external identifier.</value> public string ExternalId { get; set; } + /// <summary> + /// Gets or sets the media sources. + /// </summary> + /// <value>The media sources.</value> public List<MediaSourceInfo> MediaSources { get; set; } /// <summary> diff --git a/MediaBrowser.Providers/Chapters/ChapterManager.cs b/MediaBrowser.Providers/Chapters/ChapterManager.cs new file mode 100644 index 0000000000..2723d80a2e --- /dev/null +++ b/MediaBrowser.Providers/Chapters/ChapterManager.cs @@ -0,0 +1,240 @@ +using MediaBrowser.Common.Extensions; +using MediaBrowser.Controller.Chapters; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Chapters; +using MediaBrowser.Model.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.Chapters +{ + public class ChapterManager : IChapterManager + { + private IChapterProvider[] _providers; + private readonly ILibraryManager _libraryManager; + private readonly ILogger _logger; + private readonly IServerConfigurationManager _config; + + public ChapterManager(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config) + { + _libraryManager = libraryManager; + _logger = logger; + _config = config; + } + + public void AddParts(IEnumerable<IChapterProvider> chapterProviders) + { + _providers = chapterProviders.ToArray(); + } + + public Task<IEnumerable<RemoteChapterResult>> Search(Video video, CancellationToken cancellationToken) + { + VideoContentType mediaType; + + if (video is Episode) + { + mediaType = VideoContentType.Episode; + } + else if (video is Movie) + { + mediaType = VideoContentType.Movie; + } + else + { + // These are the only supported types + return Task.FromResult<IEnumerable<RemoteChapterResult>>(new List<RemoteChapterResult>()); + } + + var request = new ChapterSearchRequest + { + ContentType = mediaType, + IndexNumber = video.IndexNumber, + //Language = language, + MediaPath = video.Path, + Name = video.Name, + ParentIndexNumber = video.ParentIndexNumber, + ProductionYear = video.ProductionYear, + ProviderIds = video.ProviderIds, + RuntimeTicks = video.RunTimeTicks + }; + + var episode = video as Episode; + + if (episode != null) + { + request.IndexNumberEnd = episode.IndexNumberEnd; + request.SeriesName = episode.SeriesName; + } + + return Search(request, cancellationToken); + } + + public async Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, CancellationToken cancellationToken) + { + var contentType = request.ContentType; + var providers = GetInternalProviders(false) + .Where(i => i.SupportedMediaTypes.Contains(contentType)) + .ToList(); + + // If not searching all, search one at a time until something is found + if (!request.SearchAllProviders) + { + foreach (var provider in providers) + { + try + { + return await Search(request, provider, cancellationToken).ConfigureAwait(false); + + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading subtitles from {0}", ex, provider.Name); + } + } + return new List<RemoteChapterResult>(); + } + + var tasks = providers.Select(async i => + { + try + { + return await Search(request, i, cancellationToken).ConfigureAwait(false); + } + catch (Exception ex) + { + _logger.ErrorException("Error downloading subtitles from {0}", ex, i.Name); + return new List<RemoteChapterResult>(); + } + }); + + var results = await Task.WhenAll(tasks).ConfigureAwait(false); + + return results.SelectMany(i => i); + } + + private async Task<IEnumerable<RemoteChapterResult>> Search(ChapterSearchRequest request, + IChapterProvider provider, + CancellationToken cancellationToken) + { + var searchResults = await provider.Search(request, cancellationToken).ConfigureAwait(false); + + foreach (var result in searchResults) + { + result.Id = GetProviderId(provider.Name) + "_" + result.Id; + result.ProviderName = provider.Name; + } + + return searchResults; + } + + public Task<ChapterResponse> GetChapters(string id, CancellationToken cancellationToken) + { + var parts = id.Split(new[] { '_' }, 2); + + var provider = GetProvider(parts.First()); + id = parts.Last(); + + return provider.GetChapters(id, cancellationToken); + } + + public IEnumerable<ChapterProviderInfo> GetProviders(string itemId) + { + var video = _libraryManager.GetItemById(itemId) as Video; + VideoContentType mediaType; + + if (video is Episode) + { + mediaType = VideoContentType.Episode; + } + else if (video is Movie) + { + mediaType = VideoContentType.Movie; + } + else + { + // These are the only supported types + return new List<ChapterProviderInfo>(); + } + + var providers = GetInternalProviders(false) + .Where(i => i.SupportedMediaTypes.Contains(mediaType)); + + return GetInfos(providers); + } + + public IEnumerable<ChapterProviderInfo> GetProviders() + { + return GetInfos(GetInternalProviders(true)); + } + + private IEnumerable<IChapterProvider> GetInternalProviders(bool includeDisabledProviders) + { + var providers = _providers; + + if (!includeDisabledProviders) + { + providers = providers + .Where(i => _config.Configuration.ChapterOptions.DisabledFetchers.Contains(i.Name)) + .ToArray(); + } + + return providers + .OrderBy(GetConfiguredOrder) + .ThenBy(GetDefaultOrder) + .ToArray(); + } + + private IEnumerable<ChapterProviderInfo> GetInfos(IEnumerable<IChapterProvider> providers) + { + return providers.Select(i => new ChapterProviderInfo + { + Name = i.Name, + Id = GetProviderId(i.Name) + }); + } + + private string GetProviderId(string name) + { + return name.ToLower().GetMD5().ToString("N"); + } + + private IChapterProvider GetProvider(string id) + { + return _providers.First(i => string.Equals(id, GetProviderId(i.Name))); + } + + private int GetConfiguredOrder(IChapterProvider provider) + { + // See if there's a user-defined order + var index = Array.IndexOf(_config.Configuration.ChapterOptions.FetcherOrder, provider.Name); + + if (index != -1) + { + return index; + } + + // Not configured. Just return some high number to put it at the end. + return 100; + } + + private int GetDefaultOrder(IChapterProvider provider) + { + var hasOrder = provider as IHasOrder; + + if (hasOrder != null) + { + return hasOrder.Order; + } + + return 0; + } + } +} diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 1f58000fef..e40f49acfc 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -271,7 +271,7 @@ namespace MediaBrowser.Providers.Manager foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider)) { - await RunCustomProvider(provider, item, options.DirectoryService, refreshResult, cancellationToken).ConfigureAwait(false); + await RunCustomProvider(provider, item, options, refreshResult, cancellationToken).ConfigureAwait(false); } var temp = CreateNew(); @@ -343,19 +343,19 @@ namespace MediaBrowser.Providers.Manager foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider))) { - await RunCustomProvider(provider, item, options.DirectoryService, refreshResult, cancellationToken).ConfigureAwait(false); + await RunCustomProvider(provider, item, options, refreshResult, cancellationToken).ConfigureAwait(false); } return refreshResult; } - private async Task RunCustomProvider(ICustomMetadataProvider<TItemType> provider, TItemType item, IDirectoryService directoryService, RefreshResult refreshResult, CancellationToken cancellationToken) + private async Task RunCustomProvider(ICustomMetadataProvider<TItemType> provider, TItemType item, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken) { Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name); try { - refreshResult.UpdateType = refreshResult.UpdateType | await provider.FetchAsync(item, directoryService, cancellationToken).ConfigureAwait(false); + refreshResult.UpdateType = refreshResult.UpdateType | await provider.FetchAsync(item, options, cancellationToken).ConfigureAwait(false); } catch (OperationCanceledException) { diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index ce5f8aa28b..3ad4a608ab 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -79,6 +79,7 @@ <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" /> <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" /> <Compile Include="Channels\ChannelMetadataService.cs" /> + <Compile Include="Chapters\ChapterManager.cs" /> <Compile Include="Folders\CollectionFolderImageProvider.cs" /> <Compile Include="Folders\FolderMetadataService.cs" /> <Compile Include="Folders\ImagesByNameImageProvider.cs" /> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index bbbcaeedf6..9e9936cab4 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -1,5 +1,6 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; @@ -49,58 +50,59 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; + private readonly IChapterManager _chapterManager; public string Name { get { return "ffprobe"; } } - public Task<ItemUpdateType> FetchAsync(Episode item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Episode item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(MusicVideo item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(MusicVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Movie item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Movie item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(AdultVideo item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(AdultVideo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(LiveTvVideoRecording item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(LiveTvVideoRecording item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Trailer item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Trailer item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Video item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Video item, MetadataRefreshOptions options, CancellationToken cancellationToken) { - return FetchVideoInfo(item, directoryService, cancellationToken); + return FetchVideoInfo(item, options, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(Audio item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Audio item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, cancellationToken); } - public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(LiveTvAudioRecording item, MetadataRefreshOptions options, CancellationToken cancellationToken) { return FetchAudioInfo(item, cancellationToken); } - public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager) + public FFProbeProvider(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager) { _logger = logger; _isoManager = isoManager; @@ -114,10 +116,11 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; _config = config; _subtitleManager = subtitleManager; + _chapterManager = chapterManager; } private readonly Task<ItemUpdateType> _cachedTask = Task.FromResult(ItemUpdateType.None); - public Task<ItemUpdateType> FetchVideoInfo<T>(T item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchVideoInfo<T>(T item, MetadataRefreshOptions options, CancellationToken cancellationToken) where T : Video { if (item.LocationType != LocationType.FileSystem) @@ -140,9 +143,9 @@ namespace MediaBrowser.Providers.MediaInfo return _cachedTask; } - var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager); + var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager, _chapterManager); - return prober.ProbeVideo(item, directoryService, true, cancellationToken); + return prober.ProbeVideo(item, options, cancellationToken); } public Task<ItemUpdateType> FetchAudioInfo<T>(T item, CancellationToken cancellationToken) @@ -171,9 +174,10 @@ namespace MediaBrowser.Providers.MediaInfo if (video != null && !video.IsPlaceHolder) { - var prober = new FFProbeVideoInfo(_logger, _isoManager, _mediaEncoder, _itemRepo, _blurayExaminer, _localization, _appPaths, _json, _encodingManager, _fileSystem, _config, _subtitleManager); - - return !video.SubtitleFiles.SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, false).Select(i => i.FullName).OrderBy(i => i), StringComparer.OrdinalIgnoreCase); + return !video.SubtitleFiles + .SequenceEqual(SubtitleResolver.GetSubtitleFiles(video, directoryService, false) + .Select(i => i.FullName) + .OrderBy(i => i), StringComparer.OrdinalIgnoreCase); } } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 1cc04ed2a2..61610a5f1a 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; @@ -41,10 +42,11 @@ namespace MediaBrowser.Providers.MediaInfo private readonly IFileSystem _fileSystem; private readonly IServerConfigurationManager _config; private readonly ISubtitleManager _subtitleManager; + private readonly IChapterManager _chapterManager; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager) + public FFProbeVideoInfo(ILogger logger, IIsoManager isoManager, IMediaEncoder mediaEncoder, IItemRepository itemRepo, IBlurayExaminer blurayExaminer, ILocalizationManager localization, IApplicationPaths appPaths, IJsonSerializer json, IEncodingManager encodingManager, IFileSystem fileSystem, IServerConfigurationManager config, ISubtitleManager subtitleManager, IChapterManager chapterManager) { _logger = logger; _isoManager = isoManager; @@ -58,9 +60,12 @@ namespace MediaBrowser.Providers.MediaInfo _fileSystem = fileSystem; _config = config; _subtitleManager = subtitleManager; + _chapterManager = chapterManager; } - public async Task<ItemUpdateType> ProbeVideo<T>(T item, IDirectoryService directoryService, bool enableSubtitleDownloading, CancellationToken cancellationToken) + public async Task<ItemUpdateType> ProbeVideo<T>(T item, + MetadataRefreshOptions options, + CancellationToken cancellationToken) where T : Video { var isoMount = await MountIsoIfNeeded(item, cancellationToken).ConfigureAwait(false); @@ -105,7 +110,7 @@ namespace MediaBrowser.Providers.MediaInfo cancellationToken.ThrowIfCancellationRequested(); - await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, directoryService, enableSubtitleDownloading).ConfigureAwait(false); + await Fetch(item, cancellationToken, result, isoMount, blurayDiscInfo, options).ConfigureAwait(false); } finally @@ -121,7 +126,9 @@ namespace MediaBrowser.Providers.MediaInfo private const string SchemaVersion = "1"; - private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) + private async Task<InternalMediaInfoResult> GetMediaInfo(BaseItem item, + IIsoMount isoMount, + CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); @@ -160,7 +167,12 @@ namespace MediaBrowser.Providers.MediaInfo return result; } - protected async Task Fetch(Video video, CancellationToken cancellationToken, InternalMediaInfoResult data, IIsoMount isoMount, BlurayDiscInfo blurayInfo, IDirectoryService directoryService, bool enableSubtitleDownloading) + protected async Task Fetch(Video video, + CancellationToken cancellationToken, + InternalMediaInfoResult data, + IIsoMount isoMount, + BlurayDiscInfo blurayInfo, + MetadataRefreshOptions options) { var mediaInfo = MediaEncoderHelpers.GetMediaInfo(data); var mediaStreams = mediaInfo.MediaStreams; @@ -208,17 +220,12 @@ namespace MediaBrowser.Providers.MediaInfo FetchBdInfo(video, chapters, mediaStreams, blurayInfo); } - await AddExternalSubtitles(video, mediaStreams, directoryService, enableSubtitleDownloading, cancellationToken).ConfigureAwait(false); + await AddExternalSubtitles(video, mediaStreams, options, cancellationToken).ConfigureAwait(false); FetchWtvInfo(video, data); video.IsHD = mediaStreams.Any(i => i.Type == MediaStreamType.Video && i.Width.HasValue && i.Width.Value >= 1270); - if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) - { - AddDummyChapters(video, chapters); - } - var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); video.VideoBitRate = videoStream == null ? null : videoStream.BitRate; @@ -228,18 +235,34 @@ namespace MediaBrowser.Providers.MediaInfo ExtractTimestamp(video); - await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions + await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); + + if (options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || + options.MetadataRefreshMode == MetadataRefreshMode.EnsureMetadata) { - Chapters = chapters, - Video = video, - ExtractImages = false, - SaveChapters = false + var remoteChapters = await DownloadChapters(video, cancellationToken).ConfigureAwait(false); - }, cancellationToken).ConfigureAwait(false); + if (remoteChapters.Count > 0) + { + chapters = remoteChapters; + } - await _itemRepo.SaveMediaStreams(video.Id, mediaStreams, cancellationToken).ConfigureAwait(false); + if (chapters.Count == 0 && mediaStreams.Any(i => i.Type == MediaStreamType.Video)) + { + AddDummyChapters(video, chapters); + } + + await _encodingManager.RefreshChapterImages(new ChapterImageRefreshOptions + { + Chapters = chapters, + Video = video, + ExtractImages = false, + SaveChapters = false - await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); + }, cancellationToken).ConfigureAwait(false); + + await _itemRepo.SaveChapters(video.Id, chapters, cancellationToken).ConfigureAwait(false); + } } private ChapterInfo GetChapterInfo(MediaChapter chapter) @@ -416,15 +439,20 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> /// <param name="video">The video.</param> /// <param name="currentStreams">The current streams.</param> - /// <param name="directoryService">The directory service.</param> - /// <param name="enableSubtitleDownloading">if set to <c>true</c> [enable subtitle downloading].</param> + /// <param name="options">The options.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> - private async Task AddExternalSubtitles(Video video, List<MediaStream> currentStreams, IDirectoryService directoryService, bool enableSubtitleDownloading, CancellationToken cancellationToken) + private async Task AddExternalSubtitles(Video video, + List<MediaStream> currentStreams, + MetadataRefreshOptions options, + CancellationToken cancellationToken) { var subtitleResolver = new SubtitleResolver(_localization); - var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, directoryService, false).ToList(); + var externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, options.DirectoryService, false).ToList(); + + var enableSubtitleDownloading = options.MetadataRefreshMode == MetadataRefreshMode.EnsureMetadata || + options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh; if (enableSubtitleDownloading && (_config.Configuration.SubtitleOptions.DownloadEpisodeSubtitles && video is Episode) || @@ -444,7 +472,7 @@ namespace MediaBrowser.Providers.MediaInfo // Rescan if (downloadedLanguages.Count > 0) { - externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, directoryService, true).ToList(); + externalSubtitleStreams = subtitleResolver.GetExternalSubtitleStreams(video, currentStreams.Count, options.DirectoryService, true).ToList(); } } @@ -453,6 +481,33 @@ namespace MediaBrowser.Providers.MediaInfo currentStreams.AddRange(externalSubtitleStreams); } + private async Task<List<ChapterInfo>> DownloadChapters(Video video, CancellationToken cancellationToken) + { + if ((_config.Configuration.ChapterOptions.DownloadEpisodeChapters && + video is Episode) || + (_config.Configuration.ChapterOptions.DownloadMovieChapters && + video is Movie)) + { + var results = await _chapterManager.Search(video, cancellationToken).ConfigureAwait(false); + + var result = results.FirstOrDefault(); + + if (result != null) + { + var chapters = await _chapterManager.GetChapters(result.Id, cancellationToken).ConfigureAwait(false); + + return chapters.Chapters.Select(i => new ChapterInfo + { + Name = i.Name, + StartPositionTicks = i.StartPositionTicks + + }).ToList(); + } + } + + return new List<ChapterInfo>(); + } + /// <summary> /// The dummy chapter duration /// </summary> @@ -499,6 +554,7 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> /// <param name="item">The item.</param> /// <param name="mount">The mount.</param> + /// <param name="blurayDiscInfo">The bluray disc information.</param> private void OnPreFetch(Video item, IIsoMount mount, BlurayDiscInfo blurayDiscInfo) { if (item.VideoType == VideoType.Iso) diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs index a89919d3f8..c0f53e0c37 100644 --- a/MediaBrowser.Providers/Photos/PhotoProvider.cs +++ b/MediaBrowser.Providers/Photos/PhotoProvider.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Photos _imageProcessor = imageProcessor; } - public Task<ItemUpdateType> FetchAsync(Photo item, IDirectoryService directoryService, CancellationToken cancellationToken) + public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken) { item.SetImagePath(ImageType.Primary, item.Path); item.SetImagePath(ImageType.Backdrop, item.Path); diff --git a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs index 9f7a3c8e50..625dd7e82d 100644 --- a/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs @@ -410,6 +410,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv { stream.PacketLength = null; } + + // Don't trust the provider values + stream.Index = -1; } } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index 9e51a40b04..52dd8209ab 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -143,5 +143,7 @@ "HeaderSelectChannelDownloadPathHelp": "Browse or enter the path to use for storing channel cache files. The folder must be writeable.", "OptionNewCollection": "New...", "ButtonAdd": "Add", - "ButtonRemove": "Remove" + "ButtonRemove": "Remove", + "LabelChapterDownloaders": "Chapter downloaders:", + "LabelChapterDownloadersHelp": "Enable and rank your preferred chapter downloaders in order of priority. Lower priority downloaders will only be used to fill in missing information." }
\ No newline at end of file diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index b886d4f6b9..74ac9aeeb9 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -733,11 +733,15 @@ "OptionReportByteRangeSeekingWhenTranscodingHelp": "This is required for some devices that don't time seek very well.", "HeaderSubtitleDownloadingHelp": "When Media Browser scans your video files it can search for missing subtitles, and download them using a subtitle provider such as OpenSubtitles.org.", "HeaderDownloadSubtitlesFor": "Download subtitles for:", + "MessageNoChapterProviders": "Install a chapter provider plugin such as ChapterDb or tagChimp to enable additional chapter metadata options.", "LabelSkipIfGraphicalSubsPresent": "Skip if the video already contains graphical subtitles", "LabelSkipIfGraphicalSubsPresentHelp": "Keeping text versions of subtitles will result in more efficient delivery to mobile clients.", "TabSubtitles": "Subtitles", + "TabChapters": "Chapters", + "HeaderDownloadChaptersFor": "Download chapter names for:", "LabelOpenSubtitlesUsername": "Open Subtitles username:", "LabelOpenSubtitlesPassword": "Open Subtitles password:", + "HeaderChapterDownloadingHelp": "When Media Browser scans your video files it can download friendly chapter names from the internet using chapter plugins such as ChapterDb and tagChimp.", "LabelPlayDefaultAudioTrack": "Play default audio track regardless of language", "LabelSubtitlePlaybackMode": "Subtitle mode:", "LabelDownloadLanguages": "Download languages:", diff --git a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs index 73ab50ef6f..ee3e154755 100644 --- a/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs +++ b/MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs @@ -91,21 +91,21 @@ namespace MediaBrowser.Server.Implementations.MediaEncoder if (video is Movie) { - if (!_config.Configuration.EnableMovieChapterImageExtraction) + if (!_config.Configuration.ChapterOptions.EnableMovieChapterImageExtraction) { return false; } } else if (video is Episode) { - if (!_config.Configuration.EnableEpisodeChapterImageExtraction) + if (!_config.Configuration.ChapterOptions.EnableEpisodeChapterImageExtraction) { return false; } } else { - if (!_config.Configuration.EnableOtherVideoChapterImageExtraction) + if (!_config.Configuration.ChapterOptions.EnableOtherVideoChapterImageExtraction) { return false; } diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index e754b7d9db..c4d6bce4d4 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -10,6 +10,7 @@ using MediaBrowser.Common.Net; using MediaBrowser.Common.Progress; using MediaBrowser.Controller; using MediaBrowser.Controller.Channels; +using MediaBrowser.Controller.Chapters; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Dlna; @@ -43,6 +44,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.System; using MediaBrowser.Model.Updates; +using MediaBrowser.Providers.Chapters; using MediaBrowser.Providers.Manager; using MediaBrowser.Providers.Subtitles; using MediaBrowser.Server.Implementations; @@ -196,6 +198,7 @@ namespace MediaBrowser.ServerApplication private INotificationManager NotificationManager { get; set; } private ISubtitleManager SubtitleManager { get; set; } + private IChapterManager ChapterManager { get; set; } private IUserViewManager UserViewManager { get; set; } @@ -544,6 +547,9 @@ namespace MediaBrowser.ServerApplication SubtitleManager = new SubtitleManager(LogManager.GetLogger("SubtitleManager"), FileSystemManager, LibraryMonitor, LibraryManager, ItemRepository); RegisterSingleInstance(SubtitleManager); + ChapterManager = new ChapterManager(LibraryManager, LogManager.GetLogger("ChapterManager"), ServerConfigurationManager); + RegisterSingleInstance(ChapterManager); + var displayPreferencesTask = Task.Run(async () => await ConfigureDisplayPreferencesRepositories().ConfigureAwait(false)); var itemsTask = Task.Run(async () => await ConfigureItemRepositories().ConfigureAwait(false)); var userdataTask = Task.Run(async () => await ConfigureUserDataRepositories().ConfigureAwait(false)); @@ -725,7 +731,8 @@ namespace MediaBrowser.ServerApplication LiveTvManager.AddParts(GetExports<ILiveTvService>()); SubtitleManager.AddParts(GetExports<ISubtitleProvider>()); - + ChapterManager.AddParts(GetExports<IChapterProvider>()); + SessionManager.AddParts(GetExports<ISessionControllerFactory>()); ChannelManager.AddParts(GetExports<IChannel>(), GetExports<IChannelFactory>()); diff --git a/MediaBrowser.WebDashboard/Api/DashboardService.cs b/MediaBrowser.WebDashboard/Api/DashboardService.cs index 7946e73197..78344965d5 100644 --- a/MediaBrowser.WebDashboard/Api/DashboardService.cs +++ b/MediaBrowser.WebDashboard/Api/DashboardService.cs @@ -591,6 +591,7 @@ namespace MediaBrowser.WebDashboard.Api "metadataconfigurationpage.js", "metadataimagespage.js", "metadatasubtitles.js", + "metadatachapters.js", "moviegenres.js", "moviecollections.js", "movies.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index d8fa2c9be4..709340dc00 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -331,6 +331,9 @@ <Content Include="dashboard-ui\librarypathmapping.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\metadatachapters.html">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\mypreferencesdisplay.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -670,6 +673,9 @@ <Content Include="dashboard-ui\scripts\localsettings.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="dashboard-ui\scripts\metadatachapters.js">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="dashboard-ui\scripts\mypreferencesdisplay.js">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 5137daf234..d1e669b414 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.399</version> + <version>3.0.400</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.399" /> + <dependency id="MediaBrowser.Common" version="3.0.400" /> <dependency id="NLog" version="2.1.0" /> <dependency id="SimpleInjector" version="2.5.0" /> <dependency id="sharpcompress" version="0.10.2" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index b78d6549b2..873c308edc 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.399</version> + <version>3.0.400</version> <title>MediaBrowser.Common</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 cc710265a9..e97a0c7d8a 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.399</version> + <version>3.0.400</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.399" /> + <dependency id="MediaBrowser.Common" version="3.0.400" /> </dependencies> </metadata> <files> |
