|
@@ -35,12 +35,12 @@ namespace MediaBrowser.Providers.TV
|
|
|
private readonly IZipClient _zipClient;
|
|
|
private readonly IHttpClient _httpClient;
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
+ private readonly IXmlReaderSettingsFactory _xmlSettings;
|
|
|
private readonly IServerConfigurationManager _config;
|
|
|
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
|
|
private readonly ILogger _logger;
|
|
|
private readonly ILibraryManager _libraryManager;
|
|
|
private readonly IMemoryStreamProvider _memoryStreamProvider;
|
|
|
- private readonly IXmlReaderSettingsFactory _xmlSettings;
|
|
|
private readonly ILocalizationManager _localizationManager;
|
|
|
|
|
|
public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
|
|
@@ -512,9 +512,11 @@ namespace MediaBrowser.Providers.TV
|
|
|
private async Task<IEnumerable<RemoteSearchResult>> FindSeriesInternal(string name, string language, CancellationToken cancellationToken)
|
|
|
{
|
|
|
var url = string.Format(SeriesSearchUrl, WebUtility.UrlEncode(name), NormalizeLanguage(language));
|
|
|
- var doc = new XmlDocument();
|
|
|
+ var searchResults = new List<RemoteSearchResult>();
|
|
|
+
|
|
|
+ var comparableName = GetComparableName(name);
|
|
|
|
|
|
- using (var results = await _httpClient.Get(new HttpRequestOptions
|
|
|
+ using (var stream = await _httpClient.Get(new HttpRequestOptions
|
|
|
{
|
|
|
Url = url,
|
|
|
ResourcePool = TvDbResourcePool,
|
|
@@ -522,100 +524,170 @@ namespace MediaBrowser.Providers.TV
|
|
|
|
|
|
}).ConfigureAwait(false))
|
|
|
{
|
|
|
- doc.Load(results);
|
|
|
- }
|
|
|
+ var settings = _xmlSettings.Create(false);
|
|
|
|
|
|
- var searchResults = new List<RemoteSearchResult>();
|
|
|
+ settings.CheckCharacters = false;
|
|
|
+ settings.IgnoreProcessingInstructions = true;
|
|
|
+ settings.IgnoreComments = true;
|
|
|
|
|
|
- if (doc.HasChildNodes)
|
|
|
- {
|
|
|
- var nodes = doc.SelectNodes("//Series");
|
|
|
- var comparableName = GetComparableName(name);
|
|
|
- if (nodes != null)
|
|
|
+ using (var streamReader = new StreamReader(stream, Encoding.UTF8))
|
|
|
{
|
|
|
- foreach (XmlNode node in nodes)
|
|
|
+ // Use XmlReader for best performance
|
|
|
+ using (var reader = XmlReader.Create(streamReader, settings))
|
|
|
{
|
|
|
- var searchResult = new RemoteSearchResult
|
|
|
+ reader.MoveToContent();
|
|
|
+
|
|
|
+ // Loop through each element
|
|
|
+ while (reader.Read())
|
|
|
{
|
|
|
- SearchProviderName = Name
|
|
|
- };
|
|
|
+ cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
|
|
- var titles = new List<string>();
|
|
|
+ if (reader.NodeType == XmlNodeType.Element)
|
|
|
+ {
|
|
|
+ switch (reader.Name)
|
|
|
+ {
|
|
|
+ case "Series":
|
|
|
+ {
|
|
|
+ using (var subtree = reader.ReadSubtree())
|
|
|
+ {
|
|
|
+ var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName);
|
|
|
+ if (searchResult != null)
|
|
|
+ {
|
|
|
+ searchResult.SearchProviderName = Name;
|
|
|
+ searchResults.Add(searchResult);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
|
|
|
- var nameNode = node.SelectSingleNode("./SeriesName");
|
|
|
- if (nameNode != null)
|
|
|
- {
|
|
|
- titles.Add(GetComparableName(nameNode.InnerText));
|
|
|
+ default:
|
|
|
+ reader.Skip();
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- var aliasNode = node.SelectSingleNode("./AliasNames");
|
|
|
- if (aliasNode != null)
|
|
|
- {
|
|
|
- var alias = aliasNode.InnerText.Split('|').Select(GetComparableName);
|
|
|
- titles.AddRange(alias);
|
|
|
- }
|
|
|
+ if (searchResults.Count == 0)
|
|
|
+ {
|
|
|
+ _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org.");
|
|
|
+ }
|
|
|
|
|
|
- var imdbIdNode = node.SelectSingleNode("./IMDB_ID");
|
|
|
- if (imdbIdNode != null)
|
|
|
- {
|
|
|
- var val = imdbIdNode.InnerText;
|
|
|
- if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ return searchResults;
|
|
|
+ }
|
|
|
+
|
|
|
+ private RemoteSearchResult GetSeriesSearchResultFromSubTree(XmlReader reader, string comparableName)
|
|
|
+ {
|
|
|
+ var searchResult = new RemoteSearchResult
|
|
|
+ {
|
|
|
+ SearchProviderName = Name
|
|
|
+ };
|
|
|
+
|
|
|
+ var titles = new List<string>();
|
|
|
+ string seriesId = null;
|
|
|
+
|
|
|
+ reader.MoveToContent();
|
|
|
+
|
|
|
+ while (reader.Read())
|
|
|
+ {
|
|
|
+ if (reader.NodeType == XmlNodeType.Element)
|
|
|
+ {
|
|
|
+ switch (reader.Name)
|
|
|
+ {
|
|
|
+ case "SeriesName":
|
|
|
{
|
|
|
- searchResult.SetProviderId(MetadataProviders.Imdb, val);
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
+
|
|
|
+ if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ {
|
|
|
+ titles.Add(GetComparableName(val));
|
|
|
+ }
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- var bannerNode = node.SelectSingleNode("./banner");
|
|
|
- if (bannerNode != null)
|
|
|
- {
|
|
|
- var val = bannerNode.InnerText;
|
|
|
- if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ case "AliasNames":
|
|
|
{
|
|
|
- searchResult.ImageUrl = TVUtils.BannerUrl + val;
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
+
|
|
|
+ var alias = (val ?? string.Empty).Split(new [] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(GetComparableName);
|
|
|
+ titles.AddRange(alias);
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- var airDateNode = node.SelectSingleNode("./FirstAired");
|
|
|
- if (airDateNode != null)
|
|
|
- {
|
|
|
- var val = airDateNode.InnerText;
|
|
|
- if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ case "IMDB_ID":
|
|
|
{
|
|
|
- DateTime date;
|
|
|
- if (DateTime.TryParse(val, out date))
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
+
|
|
|
+ if (!string.IsNullOrWhiteSpace(val))
|
|
|
{
|
|
|
- searchResult.ProductionYear = date.Year;
|
|
|
+ searchResult.SetProviderId(MetadataProviders.Imdb, val);
|
|
|
}
|
|
|
+ break;
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- foreach (var title in titles)
|
|
|
- {
|
|
|
- if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase))
|
|
|
+ case "banner":
|
|
|
{
|
|
|
- var id = node.SelectSingleNode("./seriesid") ??
|
|
|
- node.SelectSingleNode("./id");
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
|
|
|
- if (id != null)
|
|
|
+ if (!string.IsNullOrWhiteSpace(val))
|
|
|
{
|
|
|
- searchResult.Name = title;
|
|
|
- searchResult.SetProviderId(MetadataProviders.Tvdb, id.InnerText);
|
|
|
- searchResults.Add(searchResult);
|
|
|
+ searchResult.ImageUrl = TVUtils.BannerUrl + val;
|
|
|
}
|
|
|
break;
|
|
|
}
|
|
|
- _logger.Info("TVDb Provider - " + title + " did not match " + comparableName);
|
|
|
- }
|
|
|
+
|
|
|
+ case "FirstAired":
|
|
|
+ {
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
+
|
|
|
+ if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ {
|
|
|
+ DateTime date;
|
|
|
+ if (DateTime.TryParse(val, out date))
|
|
|
+ {
|
|
|
+ searchResult.ProductionYear = date.Year;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ case "id":
|
|
|
+ case "seriesid":
|
|
|
+ {
|
|
|
+ var val = reader.ReadElementContentAsString();
|
|
|
+
|
|
|
+ if (!string.IsNullOrWhiteSpace(val))
|
|
|
+ {
|
|
|
+ seriesId = val;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+
|
|
|
+ default:
|
|
|
+ reader.Skip();
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- if (searchResults.Count == 0)
|
|
|
+ foreach (var title in titles)
|
|
|
{
|
|
|
- _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org.");
|
|
|
+ if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase))
|
|
|
+ {
|
|
|
+ if (!string.IsNullOrWhiteSpace(seriesId))
|
|
|
+ {
|
|
|
+ searchResult.Name = title;
|
|
|
+ searchResult.SetProviderId(MetadataProviders.Tvdb, seriesId);
|
|
|
+ return searchResult;
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ _logger.Info("TVDb Provider - " + title + " did not match " + comparableName);
|
|
|
}
|
|
|
|
|
|
- return searchResults;
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|