switch some xmldocuments to xmlreaders

pull/702/head
Luke Pulverenti 11 years ago
parent ebec1e159c
commit 29cc8c1d4c

@ -1,6 +1,7 @@
using MediaBrowser.Controller; using MediaBrowser.Controller;
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
@ -21,6 +22,12 @@ namespace MediaBrowser.Api.DefaultTheme
{ {
[ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid UserId { get; set; } public Guid UserId { get; set; }
[ApiMember(Name = "ComedyGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ComedyGenre { get; set; }
[ApiMember(Name = "RomanceGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string RomanceGenre { get; set; }
} }
[Route("/MBT/DefaultTheme/Movies", "GET")] [Route("/MBT/DefaultTheme/Movies", "GET")]
@ -31,6 +38,19 @@ namespace MediaBrowser.Api.DefaultTheme
[ApiMember(Name = "FamilyRating", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")] [ApiMember(Name = "FamilyRating", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
public string FamilyRating { get; set; } public string FamilyRating { get; set; }
[ApiMember(Name = "ComedyGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string ComedyGenre { get; set; }
[ApiMember(Name = "RomanceGenre", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
public string RomanceGenre { get; set; }
}
[Route("/MBT/DefaultTheme/Home", "GET")]
public class GetHomeView : IReturn<HomeView>
{
[ApiMember(Name = "UserId", IsRequired = true, DataType = "string", ParameterType = "query", Verb = "GET")]
public Guid UserId { get; set; }
} }
public class DefaultThemeService : BaseApiService public class DefaultThemeService : BaseApiService
@ -51,6 +71,39 @@ namespace MediaBrowser.Api.DefaultTheme
_localization = localization; _localization = localization;
} }
public object Get(GetHomeView request)
{
var result = GetHomeView(request).Result;
return ToOptimizedResult(result);
}
private async Task<HomeView> GetHomeView(GetHomeView request)
{
var user = _userManager.GetUserById(request.UserId);
var allItems = user.RootFolder.GetRecursiveChildren(user)
.ToList();
var itemsWithBackdrops = allItems.Where(i => i.BackdropImagePaths.Count > 0).ToList();
var view = new HomeView();
var fields = new List<ItemFields>();
var eligibleSpotlightItems = itemsWithBackdrops
.Where(i => i is Game || i is Movie || i is Series || i is MusicArtist);
var spotlightItemTasks = FilterItemsForBackdropDisplay(eligibleSpotlightItems)
.OrderBy(i => Guid.NewGuid())
.Take(50)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user));
view.SpotlightItems = await Task.WhenAll(spotlightItemTasks).ConfigureAwait(false);
return view;
}
public object Get(GetTvView request) public object Get(GetTvView request)
{ {
var result = GetTvView(request).Result; var result = GetTvView(request).Result;
@ -72,10 +125,9 @@ namespace MediaBrowser.Api.DefaultTheme
var fields = new List<ItemFields>(); var fields = new List<ItemFields>();
var spotlightItemTasks = seriesWithBackdrops var spotlightItemTasks = FilterItemsForBackdropDisplay(seriesWithBackdrops)
.OrderByDescending(i => GetResolution(i, i.BackdropImagePaths[0]))
.Take(60)
.OrderBy(i => Guid.NewGuid()) .OrderBy(i => Guid.NewGuid())
.Take(50)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user)); .Select(i => _dtoService.GetBaseItemDto(i, fields, user));
view.SpotlightItems = await Task.WhenAll(spotlightItemTasks).ConfigureAwait(false); view.SpotlightItems = await Task.WhenAll(spotlightItemTasks).ConfigureAwait(false);
@ -88,6 +140,25 @@ namespace MediaBrowser.Api.DefaultTheme
.Take(3) .Take(3)
.ToArray(); .ToArray();
var romanceGenres = request.RomanceGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
var comedyGenres = request.ComedyGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
view.RomanceItems = seriesWithBackdrops
.Where(i => i.Genres.Any(romanceGenres.ContainsKey))
.OrderBy(i => Guid.NewGuid())
.Select(i => GetItemStub(i, ImageType.Backdrop))
.Where(i => i != null)
.Take(3)
.ToArray();
view.ComedyItems = seriesWithBackdrops
.Where(i => i.Genres.Any(comedyGenres.ContainsKey))
.OrderBy(i => Guid.NewGuid())
.Select(i => GetItemStub(i, ImageType.Backdrop))
.Where(i => i != null)
.Take(3)
.ToArray();
view.ActorItems = await GetActors(series).ConfigureAwait(false); view.ActorItems = await GetActors(series).ConfigureAwait(false);
return view; return view;
@ -137,10 +208,9 @@ namespace MediaBrowser.Api.DefaultTheme
var fields = new List<ItemFields>(); var fields = new List<ItemFields>();
var spotlightItemTasks = itemsWithBackdrops var spotlightItemTasks = FilterItemsForBackdropDisplay(itemsWithBackdrops)
.OrderByDescending(i => GetResolution(i, i.BackdropImagePaths[0]))
.Take(60)
.OrderBy(i => Guid.NewGuid()) .OrderBy(i => Guid.NewGuid())
.Take(50)
.Select(i => _dtoService.GetBaseItemDto(i, fields, user)); .Select(i => _dtoService.GetBaseItemDto(i, fields, user));
view.SpotlightItems = await Task.WhenAll(spotlightItemTasks).ConfigureAwait(false); view.SpotlightItems = await Task.WhenAll(spotlightItemTasks).ConfigureAwait(false);
@ -178,9 +248,10 @@ namespace MediaBrowser.Api.DefaultTheme
.Take(3) .Take(3)
.ToArray(); .ToArray();
var romanceGenres = new[] { "romance" }.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); var romanceGenres = request.RomanceGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
var comedyGenres = request.ComedyGenre.Split(',').ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
view.RomanticItems = moviesWithBackdrops view.RomanceItems = moviesWithBackdrops
.Where(i => i.Genres.Any(romanceGenres.ContainsKey)) .Where(i => i.Genres.Any(romanceGenres.ContainsKey))
.OrderBy(i => Guid.NewGuid()) .OrderBy(i => Guid.NewGuid())
.Select(i => GetItemStub(i, ImageType.Backdrop)) .Select(i => GetItemStub(i, ImageType.Backdrop))
@ -188,6 +259,14 @@ namespace MediaBrowser.Api.DefaultTheme
.Take(3) .Take(3)
.ToArray(); .ToArray();
view.ComedyItems = moviesWithBackdrops
.Where(i => i.Genres.Any(comedyGenres.ContainsKey))
.OrderBy(i => Guid.NewGuid())
.Select(i => GetItemStub(i, ImageType.Backdrop))
.Where(i => i != null)
.Take(3)
.ToArray();
view.HDItems = hdMovies view.HDItems = hdMovies
.Where(i => i.BackdropImagePaths.Count > 0) .Where(i => i.BackdropImagePaths.Count > 0)
.OrderBy(i => Guid.NewGuid()) .OrderBy(i => Guid.NewGuid())
@ -209,6 +288,25 @@ namespace MediaBrowser.Api.DefaultTheme
return view; return view;
} }
private IEnumerable<BaseItem> FilterItemsForBackdropDisplay(IEnumerable<BaseItem> items)
{
var tuples = items
.Select(i => new Tuple<BaseItem, double>(i, GetResolution(i, i.BackdropImagePaths[0])))
.Where(i => i.Item2 > 0)
.ToList();
var topItems = tuples
.Where(i => i.Item2 >= 1920)
.ToList();
if (topItems.Count >= 10)
{
return topItems.Select(i => i.Item1);
}
return tuples.Select(i => i.Item1);
}
private double GetResolution(BaseItem item, string path) private double GetResolution(BaseItem item, string path)
{ {
try try

@ -1,5 +1,4 @@
using System.Linq; using MediaBrowser.Common.Net;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.Movies;
@ -10,6 +9,7 @@ using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Serialization; using MediaBrowser.Model.Serialization;
using System; using System;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -46,7 +46,7 @@ namespace MediaBrowser.Providers.Movies
{ {
get get
{ {
return "8"; return "9";
} }
} }

@ -5,10 +5,9 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Model.Net;
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@ -98,7 +97,7 @@ namespace MediaBrowser.Providers.TV
{ {
get get
{ {
return "1"; return "2";
} }
} }
@ -138,22 +137,21 @@ namespace MediaBrowser.Providers.TV
var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null;
if (!string.IsNullOrEmpty(seriesId)) var seasonNumber = season.IndexNumber;
if (!string.IsNullOrEmpty(seriesId) && seasonNumber.HasValue)
{ {
// Process images // Process images
var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml");
var imagesFileInfo = new FileInfo(imagesXmlPath); try
if (imagesFileInfo.Exists)
{
if (!season.HasImage(ImageType.Primary) || !season.HasImage(ImageType.Banner) || season.BackdropImagePaths.Count == 0)
{ {
var xmlDoc = new XmlDocument(); var fanartData = FetchFanartXmlData(imagesXmlPath, seasonNumber.Value, cancellationToken);
xmlDoc.Load(imagesXmlPath); await DownloadImages(item, fanartData, ConfigurationManager.Configuration.MaxBackdrops, cancellationToken).ConfigureAwait(false);
await FetchImages(season, xmlDoc, cancellationToken).ConfigureAwait(false);
} }
catch (FileNotFoundException)
{
// No biggie. Not all series have images
} }
SetLastRefreshed(item, DateTime.UtcNow); SetLastRefreshed(item, DateTime.UtcNow);
@ -163,86 +161,169 @@ namespace MediaBrowser.Providers.TV
return false; return false;
} }
/// <summary> private async Task DownloadImages(BaseItem item, FanartXmlData data, int backdropLimit, CancellationToken cancellationToken)
/// Fetches the images.
/// </summary>
/// <param name="season">The season.</param>
/// <param name="images">The images.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task FetchImages(Season season, XmlDocument images, CancellationToken cancellationToken)
{ {
var seasonNumber = season.IndexNumber ?? -1; if (!item.HasImage(ImageType.Primary))
if (seasonNumber == -1)
{ {
return; var url = data.LanguagePoster ?? data.Poster;
if (!string.IsNullOrEmpty(url))
{
url = TVUtils.BannerUrl + url;
await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
.ConfigureAwait(false);
}
} }
if (!season.HasImage(ImageType.Primary)) if (ConfigurationManager.Configuration.DownloadSeasonImages.Banner && !item.HasImage(ImageType.Banner))
{ {
var n = images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='season'][Season='" + seasonNumber + "'][Language='" + ConfigurationManager.Configuration.PreferredMetadataLanguage + "']") ?? var url = data.LanguageBanner ?? data.Banner;
images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='season'][Season='" + seasonNumber + "'][Language='en']"); if (!string.IsNullOrEmpty(url))
if (n != null)
{ {
n = n.SelectSingleNode("./BannerPath"); url = TVUtils.BannerUrl + url;
if (n != null) await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken)
.ConfigureAwait(false);
}
}
if (ConfigurationManager.Configuration.DownloadSeasonImages.Backdrops && item.BackdropImagePaths.Count == 0)
{ {
var url = TVUtils.BannerUrl + n.InnerText; var bdNo = item.BackdropImagePaths.Count;
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken) foreach (var backdrop in data.Backdrops)
{
var url = TVUtils.BannerUrl + backdrop;
await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
bdNo++;
if (item.BackdropImagePaths.Count >= backdropLimit) break;
} }
} }
} }
if (ConfigurationManager.Configuration.DownloadSeasonImages.Banner && !season.HasImage(ImageType.Banner)) private FanartXmlData FetchFanartXmlData(string bannersXmlPath, int seasonNumber, CancellationToken cancellationToken)
{ {
var n = images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='seasonwide'][Season='" + seasonNumber + "'][Language='" + ConfigurationManager.Configuration.PreferredMetadataLanguage + "']") ?? var settings = new XmlReaderSettings
images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='seasonwide'][Season='" + seasonNumber + "'][Language='en']");
if (n != null)
{ {
n = n.SelectSingleNode("./BannerPath"); CheckCharacters = false,
if (n != null) IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
};
var data = new FanartXmlData();
using (var streamReader = new StreamReader(bannersXmlPath, Encoding.UTF8))
{ {
try // Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{ {
var url = TVUtils.BannerUrl + n.InnerText; reader.MoveToContent();
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken) // Loop through each element
.ConfigureAwait(false); while (reader.Read())
}
catch (HttpException ex)
{ {
Logger.ErrorException("Error downloading season banner for {0}", ex, season.Path); cancellationToken.ThrowIfCancellationRequested();
// Sometimes banners will come up not found even though they're reported in tvdb xml if (reader.NodeType == XmlNodeType.Element)
if (ex.StatusCode.HasValue && ex.StatusCode.Value != HttpStatusCode.NotFound) {
switch (reader.Name)
{
case "Banner":
{
using (var subtree = reader.ReadSubtree())
{ {
throw; FetchInfoFromBannerNode(data, subtree, seasonNumber);
}
break;
}
default:
reader.Skip();
break;
} }
} }
} }
} }
} }
if (ConfigurationManager.Configuration.DownloadSeasonImages.Backdrops && season.BackdropImagePaths.Count == 0) return data;
}
private void FetchInfoFromBannerNode(FanartXmlData data, XmlReader reader, int seasonNumber)
{ {
var n = images.SelectSingleNode("//Banner[BannerType='fanart'][Season='" + seasonNumber + "']"); reader.MoveToContent();
if (n != null)
string bannerType = null;
string bannerType2 = null;
string url = null;
int? bannerSeason = null;
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{ {
n = n.SelectSingleNode("./BannerPath"); switch (reader.Name)
if (n != null)
{ {
var url = TVUtils.BannerUrl + n.InnerText; case "BannerType":
{
bannerType = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
await _providerManager.SaveImage(season, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, 0, cancellationToken) case "BannerType2":
.ConfigureAwait(false); {
bannerType2 = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerPath":
{
url = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "Season":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
bannerSeason = int.Parse(val);
}
break;
}
default:
reader.Skip();
break;
} }
} }
} }
if (!string.IsNullOrEmpty(url) && bannerSeason.HasValue && bannerSeason.Value == seasonNumber)
{
if (string.Equals(bannerType, "season", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(bannerType2, "season", StringComparison.OrdinalIgnoreCase))
{
data.Poster = url;
} }
else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase))
{
data.Banner = url;
}
}
else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase))
{
data.Backdrops.Add(url);
}
}
}
} }
} }

@ -247,10 +247,7 @@ namespace MediaBrowser.Providers.TV
if (!series.LockedFields.Contains(MetadataFields.Cast)) if (!series.LockedFields.Contains(MetadataFields.Cast))
{ {
var actorsDoc = new XmlDocument(); FetchActors(series, actorsXmlPath, cancellationToken);
actorsDoc.Load(actorsXmlPath);
FetchActors(series, actorsDoc, seriesDoc);
} }
} }
} }
@ -384,8 +381,11 @@ namespace MediaBrowser.Providers.TV
} }
} }
} }
series.OfficialRating = doc.SafeGetString("//ContentRating"); series.OfficialRating = doc.SafeGetString("//ContentRating");
if (!series.LockedFields.Contains(MetadataFields.Genres))
// Only fill this in if there's no existing genres, because Imdb data from Omdb is preferred
if (!series.LockedFields.Contains(MetadataFields.Genres) && (series.Genres.Count == 0 || !string.Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase)))
{ {
string g = doc.SafeGetString("//Genre"); string g = doc.SafeGetString("//Genre");
@ -429,31 +429,94 @@ namespace MediaBrowser.Providers.TV
/// Fetches the actors. /// Fetches the actors.
/// </summary> /// </summary>
/// <param name="series">The series.</param> /// <param name="series">The series.</param>
/// <param name="actorsDoc">The actors doc.</param> /// <param name="actorsXmlPath">The actors XML path.</param>
/// <param name="seriesDoc">The seriesDoc.</param> /// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns> private void FetchActors(Series series, string actorsXmlPath, CancellationToken cancellationToken)
private void FetchActors(Series series, XmlDocument actorsDoc, XmlDocument seriesDoc)
{ {
var xmlNodeList = actorsDoc.SelectNodes("Actors/Actor"); 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))
{
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())
{
FetchDataFromActorNode(series, subtree);
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
}
}
if (xmlNodeList != null) /// <summary>
/// Fetches the data from actor node.
/// </summary>
/// <param name="series">The series.</param>
/// <param name="reader">The reader.</param>
private void FetchDataFromActorNode(Series series, XmlReader reader)
{ {
series.People.Clear(); reader.MoveToContent();
var personInfo = new PersonInfo();
foreach (XmlNode p in xmlNodeList) while (reader.Read())
{ {
string actorName = p.SafeGetString("Name"); if (reader.NodeType == XmlNodeType.Element)
string actorRole = p.SafeGetString("Role"); {
switch (reader.Name)
{
case "Name":
{
personInfo.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
break;
}
if (!string.IsNullOrWhiteSpace(actorName)) case "Role":
{ {
// Sometimes tvdb actors have leading spaces personInfo.Role = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
series.AddPerson(new PersonInfo { Type = PersonType.Actor, Name = actorName.Trim(), Role = actorRole }); break;
} }
default:
reader.Skip();
break;
} }
} }
} }
if (!string.IsNullOrEmpty(personInfo.Name))
{
series.AddPerson(personInfo);
}
}
/// <summary> /// <summary>
/// The us culture /// The us culture
/// </summary> /// </summary>

@ -5,10 +5,10 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Providers.Extensions;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@ -87,38 +87,114 @@ namespace MediaBrowser.Providers.TV
var actorXmlPath = Path.Combine(tvdbPath, "actors.xml"); var actorXmlPath = Path.Combine(tvdbPath, "actors.xml");
var xmlDoc = new XmlDocument(); var url = FetchImageUrl(item, actorXmlPath, cancellationToken);
xmlDoc.Load(actorXmlPath); if (!string.IsNullOrEmpty(url))
{
url = TVUtils.BannerUrl + url;
var actorNodes = xmlDoc.SelectNodes("//Actor"); await _providerManager.SaveImage(item, url, RemoteSeriesProvider.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
};
if (actorNodes == null) using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8))
{ {
return; // Use XmlReader for best performance
} using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
foreach (var actorNode in actorNodes.OfType<XmlNode>()) // Loop through each element
while (reader.Read())
{ {
var name = actorNode.SafeGetString("Name"); cancellationToken.ThrowIfCancellationRequested();
if (string.Equals(item.Name, name, StringComparison.OrdinalIgnoreCase)) if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Actor":
{
using (var subtree = reader.ReadSubtree())
{ {
var image = actorNode.SafeGetString("Image"); var url = FetchImageUrlFromActorNode(item, subtree);
if (!string.IsNullOrEmpty(image)) if (!string.IsNullOrEmpty(url))
{ {
var url = TVUtils.BannerUrl + image; return url;
}
}
break;
}
default:
reader.Skip();
break;
}
}
}
}
}
await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, return null;
ImageType.Primary, null, cancellationToken).ConfigureAwait(false);
} }
/// <summary>
/// Fetches the data from actor node.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="reader">The reader.</param>
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; break;
} }
} }
} }
if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) &&
string.Equals(name, item.Name, StringComparison.OrdinalIgnoreCase))
{
return image;
}
return null;
}
public override MetadataProviderPriority Priority public override MetadataProviderPriority Priority
{ {
get { return MetadataProviderPriority.Third; } get { return MetadataProviderPriority.Third; }

@ -11,7 +11,6 @@ using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
using MediaBrowser.Providers.Extensions;
namespace MediaBrowser.Providers.TV namespace MediaBrowser.Providers.TV
{ {
@ -102,11 +101,7 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {
var doc = new XmlDocument(); newUpdateTime = GetUpdateTime(stream);
doc.Load(stream);
newUpdateTime = doc.SafeGetString("//Time");
} }
await UpdateSeries(existingDirectories, path, progress, cancellationToken).ConfigureAwait(false); await UpdateSeries(existingDirectories, path, progress, cancellationToken).ConfigureAwait(false);
@ -124,6 +119,51 @@ namespace MediaBrowser.Providers.TV
progress.Report(100); progress.Report(100);
} }
/// <summary>
/// Gets the update time.
/// </summary>
/// <param name="response">The response.</param>
/// <returns>System.String.</returns>
private string GetUpdateTime(Stream response)
{
var settings = new XmlReaderSettings
{
CheckCharacters = false,
IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
};
using (var streamReader = new StreamReader(response, Encoding.UTF8))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
// Loop through each element
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Time":
{
return (reader.ReadElementContentAsString() ?? string.Empty).Trim();
}
default:
reader.Skip();
break;
}
}
}
}
}
return null;
}
/// <summary> /// <summary>
/// Gets the series ids to update. /// Gets the series ids to update.
/// </summary> /// </summary>
@ -143,24 +183,66 @@ namespace MediaBrowser.Providers.TV
}).ConfigureAwait(false)) }).ConfigureAwait(false))
{ {
var doc = new XmlDocument(); var data = GetUpdatedSeriesIdList(stream);
doc.Load(stream); var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
var newUpdateTime = doc.SafeGetString("//Time"); var seriesList = data.Item1
.Where(i => !string.IsNullOrWhiteSpace(i) && existingDictionary.ContainsKey(i));
var seriesNodes = doc.SelectNodes("//Series"); return new Tuple<IEnumerable<string>, string>(seriesList, data.Item2);
}
}
var existingDictionary = existingSeriesIds.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); private Tuple<List<string>, string> GetUpdatedSeriesIdList(Stream stream)
{
string updateTime = null;
var idList = new List<string>();
var seriesList = seriesNodes == null ? new string[] { } : var settings = new XmlReaderSettings
seriesNodes.Cast<XmlNode>() {
.Select(i => i.InnerText) CheckCharacters = false,
.Where(i => !string.IsNullOrWhiteSpace(i) && existingDictionary.ContainsKey(i)); IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
};
using (var streamReader = new StreamReader(stream, Encoding.UTF8))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
return new Tuple<IEnumerable<string>, string>(seriesList, newUpdateTime); // Loop through each element
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Time":
{
updateTime = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
break;
}
case "Series":
{
var id = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
idList.Add(id);
break;
}
default:
reader.Skip();
break;
} }
} }
}
}
}
return new Tuple<List<string>, string>(idList, updateTime);
}
/// <summary> /// <summary>
/// Updates the series. /// Updates the series.

@ -7,8 +7,10 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Xml; using System.Xml;
@ -150,16 +152,18 @@ namespace MediaBrowser.Providers.TV
// Process images // Process images
var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml");
var imagesFileInfo = new FileInfo(imagesXmlPath);
if (imagesFileInfo.Exists)
{
if (!series.HasImage(ImageType.Primary) || !series.HasImage(ImageType.Banner) || series.BackdropImagePaths.Count == 0) if (!series.HasImage(ImageType.Primary) || !series.HasImage(ImageType.Banner) || series.BackdropImagePaths.Count == 0)
{ {
var xmlDoc = new XmlDocument(); var backdropLimit = ConfigurationManager.Configuration.MaxBackdrops;
xmlDoc.Load(imagesXmlPath);
await FetchImages(series, xmlDoc, cancellationToken).ConfigureAwait(false); try
{
var fanartData = FetchFanartXmlData(imagesXmlPath, backdropLimit, cancellationToken);
await DownloadImages(item, fanartData, backdropLimit, cancellationToken).ConfigureAwait(false);
}
catch (FileNotFoundException)
{
// No biggie. Not all series have images
} }
} }
@ -179,72 +183,183 @@ namespace MediaBrowser.Providers.TV
protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
/// <summary> private async Task DownloadImages(BaseItem item, FanartXmlData data, int backdropLimit, CancellationToken cancellationToken)
/// Fetches the images.
/// </summary>
/// <param name="series">The series.</param>
/// <param name="images">The images.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task.</returns>
private async Task FetchImages(Series series, XmlDocument images, CancellationToken cancellationToken)
{ {
if (!series.HasImage(ImageType.Primary)) if (!item.HasImage(ImageType.Primary))
{ {
var n = images.SelectSingleNode("//Banner[BannerType='poster']"); var url = data.LanguagePoster ?? data.Poster;
if (n != null) if (!string.IsNullOrEmpty(url))
{
url = TVUtils.BannerUrl + url;
await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
.ConfigureAwait(false);
}
}
if (ConfigurationManager.Configuration.DownloadSeriesImages.Banner && !item.HasImage(ImageType.Banner))
{
var url = data.LanguageBanner ?? data.Banner;
if (!string.IsNullOrEmpty(url))
{
url = TVUtils.BannerUrl + url;
await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken)
.ConfigureAwait(false);
}
}
if (ConfigurationManager.Configuration.DownloadSeriesImages.Backdrops && item.BackdropImagePaths.Count == 0)
{ {
n = n.SelectSingleNode("./BannerPath"); var bdNo = item.BackdropImagePaths.Count;
if (n != null)
foreach (var backdrop in data.Backdrops)
{ {
var url = TVUtils.BannerUrl + n.InnerText; var url = TVUtils.BannerUrl + backdrop;
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken) await _providerManager.SaveImage(item, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken)
.ConfigureAwait(false); .ConfigureAwait(false);
bdNo++;
if (item.BackdropImagePaths.Count >= backdropLimit) break;
} }
} }
} }
if (ConfigurationManager.Configuration.DownloadSeriesImages.Banner && !series.HasImage(ImageType.Banner)) private FanartXmlData FetchFanartXmlData(string bannersXmlPath, int backdropLimit, CancellationToken cancellationToken)
{ {
var n = images.SelectSingleNode("//Banner[BannerType='series']"); var settings = new XmlReaderSettings
if (n != null)
{ {
n = n.SelectSingleNode("./BannerPath"); CheckCharacters = false,
if (n != null) IgnoreProcessingInstructions = true,
IgnoreComments = true,
ValidationType = ValidationType.None
};
var data = new FanartXmlData();
using (var streamReader = new StreamReader(bannersXmlPath, Encoding.UTF8))
{ {
var url = TVUtils.BannerUrl + n.InnerText; // Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken) // Loop through each element
.ConfigureAwait(false); while (reader.Read())
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Banner":
{
using (var subtree = reader.ReadSubtree())
{
FetchInfoFromBannerNode(data, subtree, backdropLimit);
} }
break;
} }
default:
reader.Skip();
break;
}
}
}
}
}
return data;
} }
if (series.BackdropImagePaths.Count == 0) private void FetchInfoFromBannerNode(FanartXmlData data, XmlReader reader, int backdropLimit)
{ {
var bdNo = series.BackdropImagePaths.Count; reader.MoveToContent();
string type = null;
string url = null;
var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']"); while (reader.Read())
if (xmlNodeList != null) {
if (reader.NodeType == XmlNodeType.Element)
{ {
foreach (XmlNode b in xmlNodeList) switch (reader.Name)
{ {
var p = b.SelectSingleNode("./BannerPath"); case "BannerType":
{
type = reader.ReadElementContentAsString() ?? string.Empty;
if (p != null) if (string.Equals(type, "poster", StringComparison.OrdinalIgnoreCase))
{
// Already got it
if (!string.IsNullOrEmpty(data.Poster))
{
return;
}
}
else if (string.Equals(type, "series", StringComparison.OrdinalIgnoreCase))
{
// Already got it
if (!string.IsNullOrEmpty(data.Banner))
{
return;
}
}
else if (string.Equals(type, "fanart", StringComparison.OrdinalIgnoreCase))
{
if (data.Backdrops.Count >= backdropLimit)
{
return;
}
}
else
{ {
var url = TVUtils.BannerUrl + p.InnerText; return;
}
await _providerManager.SaveImage(series, url, RemoteSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, bdNo, cancellationToken) break;
.ConfigureAwait(false); }
bdNo++; case "BannerPath":
{
url = reader.ReadElementContentAsString() ?? string.Empty;
break;
} }
if (series.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) break; default:
reader.Skip();
break;
} }
} }
} }
if (!string.IsNullOrEmpty(url))
{
if (string.Equals(type, "poster", StringComparison.OrdinalIgnoreCase))
{
data.Poster = url;
} }
else if (string.Equals(type, "series", StringComparison.OrdinalIgnoreCase))
{
data.Banner = url;
}
else if (string.Equals(type, "fanart", StringComparison.OrdinalIgnoreCase))
{
data.Backdrops.Add(url);
}
}
}
}
internal class FanartXmlData
{
public string LanguagePoster { get; set; }
public string LanguageBanner { get; set; }
public string Poster { get; set; }
public string Banner { get; set; }
public List<string> Backdrops = new List<string>();
} }
} }

Loading…
Cancel
Save