aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-06-09 15:16:14 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-06-09 15:16:14 -0400
commit945e84327087c9e81371c7b4f940a19a0c083586 (patch)
tree0c4f4c2d4c048c3baf4acfe45a7caf8ff9fe931b
parentba336372512e6366517a67e8b12677333266d032 (diff)
add new chapter provider feature
-rw-r--r--MediaBrowser.Api/Library/ChapterService.cs28
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs4
-rw-r--r--MediaBrowser.Api/UserLibrary/UserLibraryService.cs2
-rw-r--r--MediaBrowser.Common.Implementations/Security/UsageReporter.cs6
-rw-r--r--MediaBrowser.Controller/Chapters/ChapterSearchRequest.cs2
-rw-r--r--MediaBrowser.Controller/Chapters/IChapterManager.cs57
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj1
-rw-r--r--MediaBrowser.Controller/Providers/ICustomMetadataProvider.cs9
-rw-r--r--MediaBrowser.Model/Chapters/RemoteChapterInfo.cs6
-rw-r--r--MediaBrowser.Model/Chapters/RemoteChapterResult.cs6
-rw-r--r--MediaBrowser.Model/Configuration/ServerConfiguration.cs34
-rw-r--r--MediaBrowser.Model/LiveTv/ChannelInfoDto.cs4
-rw-r--r--MediaBrowser.Providers/Chapters/ChapterManager.cs240
-rw-r--r--MediaBrowser.Providers/Manager/MetadataService.cs8
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs50
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs104
-rw-r--r--MediaBrowser.Providers/Photos/PhotoProvider.cs2
-rw-r--r--MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs3
-rw-r--r--MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json4
-rw-r--r--MediaBrowser.Server.Implementations/Localization/Server/server.json4
-rw-r--r--MediaBrowser.Server.Implementations/MediaEncoder/EncodingManager.cs6
-rw-r--r--MediaBrowser.ServerApplication/ApplicationHost.cs9
-rw-r--r--MediaBrowser.WebDashboard/Api/DashboardService.cs1
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj6
-rw-r--r--Nuget/MediaBrowser.Common.Internal.nuspec4
-rw-r--r--Nuget/MediaBrowser.Common.nuspec2
-rw-r--r--Nuget/MediaBrowser.Server.Core.nuspec4
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>