add new chapter provider feature

pull/702/head
Luke Pulverenti 10 years ago
parent ba33637251
commit 945e843270

@ -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);
}
}
}

@ -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" />

@ -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:

@ -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>

@ -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);

@ -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);

@ -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();
}
}

@ -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" />

@ -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

@ -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; }
}
}

@ -21,6 +21,12 @@ namespace MediaBrowser.Model.Chapters
/// <value>The name.</value>
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>

@ -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[] { };
}
}
}

@ -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>

@ -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;
}
}
}

@ -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)
{

@ -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" />

@ -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);
}
}

@ -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)

@ -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);

@ -410,6 +410,9 @@ namespace MediaBrowser.Server.Implementations.LiveTv
{
stream.PacketLength = null;
}
// Don't trust the provider values
stream.Index = -1;
}
}

@ -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."
}

@ -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:",

@ -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;
}

@ -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>());

@ -591,6 +591,7 @@ namespace MediaBrowser.WebDashboard.Api
"metadataconfigurationpage.js",
"metadataimagespage.js",
"metadatasubtitles.js",
"metadatachapters.js",
"moviegenres.js",
"moviecollections.js",
"movies.js",

@ -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>

@ -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" />

@ -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>

@ -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>

Loading…
Cancel
Save