From 0f311f48c49d34b00a755d52257ef1aa9a985ef9 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 5 Nov 2013 11:08:44 -0500 Subject: [PATCH] #608 - Add manual image selection for People --- .../MediaBrowser.Providers.csproj | 1 + .../TV/ManualTvdbPersonImageProvider.cs | 192 ++++++++++++++++++ .../TV/TvdbPersonImageProvider.cs | 158 ++------------ .../ScheduledTasks/PeopleValidationTask.cs | 2 +- .../ScheduledTasks/RefreshMediaLibraryTask.cs | 2 +- 5 files changed, 209 insertions(+), 146 deletions(-) create mode 100644 MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 128db86b25..ba436a746a 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -113,6 +113,7 @@ + diff --git a/MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs b/MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs new file mode 100644 index 0000000000..757e16d3d6 --- /dev/null +++ b/MediaBrowser.Providers/TV/ManualTvdbPersonImageProvider.cs @@ -0,0 +1,192 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Providers; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; + +namespace MediaBrowser.Providers.TV +{ + public class ManualTvdbPersonImageProvider : IImageProvider + { + private readonly IServerConfigurationManager _config; + private readonly ILibraryManager _library; + + public ManualTvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager library) + { + _config = config; + _library = library; + } + + public string Name + { + get { return ProviderName; } + } + + public static string ProviderName + { + get { return "TheTVDB"; } + } + + public bool Supports(BaseItem item) + { + return item is Person; + } + + public async Task> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken) + { + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); + + return images.Where(i => i.Type == imageType); + } + + public Task> GetAllImages(BaseItem item, CancellationToken cancellationToken) + { + var seriesWithPerson = _library.RootFolder + .RecursiveChildren + .OfType() + .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)) && i.People.Any(p => string.Equals(p.Name, item.Name, StringComparison.OrdinalIgnoreCase))) + .ToList(); + + var infos = seriesWithPerson.Select(i => GetImageFromSeriesData(i, item.Name, cancellationToken)) + .Where(i => i != null) + .Take(1); + + return Task.FromResult(infos); + } + + private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken) + { + var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb)); + + var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); + + try + { + return GetImageInfo(actorXmlPath, personName, cancellationToken); + } + catch (FileNotFoundException) + { + return null; + } + } + + private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken) + { + var settings = new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + }; + + using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Actor": + { + using (var subtree = reader.ReadSubtree()) + { + var info = FetchImageInfoFromActorNode(personName, subtree); + + if (info != null) + { + return info; + } + } + break; + } + default: + reader.Skip(); + break; + } + } + } + } + } + + return null; + } + + /// + /// Fetches the data from actor node. + /// + /// Name of the person. + /// The reader. + /// System.String. + private RemoteImageInfo FetchImageInfoFromActorNode(string personName, XmlReader reader) + { + reader.MoveToContent(); + + string name = null; + string image = null; + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Name": + { + name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); + break; + } + + case "Image": + { + image = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); + break; + } + + default: + reader.Skip(); + break; + } + } + } + + if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) && + string.Equals(name, personName, StringComparison.OrdinalIgnoreCase)) + { + return new RemoteImageInfo + { + Url = TVUtils.BannerUrl + image, + Type = ImageType.Primary, + ProviderName = Name + + }; + } + + return null; + } + + public int Priority + { + get { return 1; } + } + } +} diff --git a/MediaBrowser.Providers/TV/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbPersonImageProvider.cs index 5882d74cfb..8c2e201aab 100644 --- a/MediaBrowser.Providers/TV/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbPersonImageProvider.cs @@ -1,29 +1,24 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Providers; using System; -using System.IO; +using System.Collections.Generic; using System.Linq; -using System.Text; using System.Threading; using System.Threading.Tasks; -using System.Xml; namespace MediaBrowser.Providers.TV { public class TvdbPersonImageProvider : BaseMetadataProvider { - private readonly ILibraryManager _library; private readonly IProviderManager _providerManager; - public TvdbPersonImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILibraryManager library, IProviderManager providerManager) + public TvdbPersonImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) : base(logManager, configurationManager) { - _library = library; _providerManager = providerManager; } @@ -67,158 +62,33 @@ namespace MediaBrowser.Providers.TV { if (string.IsNullOrEmpty(item.PrimaryImagePath)) { - var seriesWithPerson = _library.RootFolder - .RecursiveChildren - .OfType() - .Where(i => !string.IsNullOrEmpty(i.GetProviderId(MetadataProviders.Tvdb)) && i.People.Any(p => string.Equals(p.Name, item.Name, StringComparison.OrdinalIgnoreCase))) - .ToList(); + cancellationToken.ThrowIfCancellationRequested(); - foreach (var series in seriesWithPerson) - { - try - { - await DownloadImageFromSeries(item, series, cancellationToken).ConfigureAwait(false); - } - catch (FileNotFoundException) - { - // No biggie - continue; - } + var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualTvdbPersonImageProvider.ProviderName).ConfigureAwait(false); - // break once we have an image - if (!string.IsNullOrEmpty(item.PrimaryImagePath)) - { - break; - } - } + await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false); + SetLastRefreshed(item, DateTime.UtcNow); + return true; } SetLastRefreshed(item, DateTime.UtcNow); return true; } - - /// - /// Downloads the image from series. - /// - /// The item. - /// The series. - /// The cancellation token. - /// Task. - private async Task DownloadImageFromSeries(BaseItem item, Series series, CancellationToken cancellationToken) + private async Task DownloadImages(BaseItem item, List images, CancellationToken cancellationToken) { - var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, series.GetProviderId(MetadataProviders.Tvdb)); - - var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); - - var url = FetchImageUrl(item, actorXmlPath, cancellationToken); - - if (!string.IsNullOrEmpty(url)) + if (!item.HasImage(ImageType.Primary)) { - url = TVUtils.BannerUrl + url; + var image = images.FirstOrDefault(i => i.Type == ImageType.Primary); - await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, - ImageType.Primary, null, cancellationToken).ConfigureAwait(false); - } - } - private string FetchImageUrl(BaseItem item, string actorsXmlPath, CancellationToken cancellationToken) - { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; - - using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + if (image != null) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Actor": - { - using (var subtree = reader.ReadSubtree()) - { - var url = FetchImageUrlFromActorNode(item, subtree); - - if (!string.IsNullOrEmpty(url)) - { - return url; - } - } - break; - } - default: - reader.Skip(); - break; - } - } - } + await _providerManager.SaveImage(item, image.Url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken) + .ConfigureAwait(false); } } - - return null; } - /// - /// Fetches the data from actor node. - /// - /// The item. - /// The reader. - private string FetchImageUrlFromActorNode(BaseItem item, XmlReader reader) - { - reader.MoveToContent(); - - string name = null; - string image = null; - - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Name": - { - name = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - case "Image": - { - image = (reader.ReadElementContentAsString() ?? string.Empty).Trim(); - break; - } - - default: - reader.Skip(); - break; - } - } - } - - if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) && - string.Equals(name, item.Name, StringComparison.OrdinalIgnoreCase)) - { - return image; - } - - return null; - } - - public override MetadataProviderPriority Priority { get { return MetadataProviderPriority.Third; } diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs index ce15f56069..7d0593c5f1 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/PeopleValidationTask.cs @@ -36,7 +36,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks { new DailyTrigger { TimeOfDay = TimeSpan.FromHours(2) }, - new IntervalTrigger{ Interval = TimeSpan.FromHours(12)} + new IntervalTrigger{ Interval = TimeSpan.FromHours(24)} }; } diff --git a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs index a343943f76..462dc1e785 100644 --- a/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs +++ b/MediaBrowser.Server.Implementations/ScheduledTasks/RefreshMediaLibraryTask.cs @@ -40,7 +40,7 @@ namespace MediaBrowser.Server.Implementations.ScheduledTasks new SystemEventTrigger{ SystemEvent = SystemEvent.WakeFromSleep}, - new IntervalTrigger{ Interval = TimeSpan.FromHours(2)} + new IntervalTrigger{ Interval = TimeSpan.FromHours(4)} }; }