Some of it works???

pull/776/head
Claus Vium 5 years ago
parent 9729ae52a3
commit c2202be0f8

@ -10,7 +10,7 @@ namespace MediaBrowser.Controller.Library
/// <summary>
/// The TVDB API key
/// </summary>
public static readonly string TvdbApiKey = "72930AE1CB7E2DB3";
public static readonly string TvdbApiKey = "OG4V3YJ3FAP7FP2K";
public static readonly string TvdbBaseUrl = "https://www.thetvdb.com/";
/// <summary>
/// The banner URL

@ -80,22 +80,9 @@ namespace MediaBrowser.Providers.People
private RemoteImageInfo GetImageFromSeriesData(Series series, string personName, CancellationToken cancellationToken)
{
var tvdbPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds);
//var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken);
var actorXmlPath = Path.Combine(tvdbPath, "actors.xml");
try
{
return GetImageInfo(actorXmlPath, personName, cancellationToken);
}
catch (FileNotFoundException)
{
return null;
}
catch (IOException)
{
return null;
}
return null; // GetImageInfo(actorXmlPath, personName, cancellationToken);
}
private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken)

@ -44,27 +44,15 @@ namespace MediaBrowser.Providers.TV
public async Task<bool> Run(Series series, bool addNewItems, CancellationToken cancellationToken)
{
// TODO cvium fixme wtfisthisandwhydoesitrunwhenoptionisdisabled
return true;
var tvdbId = series.GetProviderId(MetadataProviders.Tvdb);
// Todo: Support series by imdb id
var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
seriesProviderIds[MetadataProviders.Tvdb.ToString()] = tvdbId;
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
// Doesn't have required provider id's
if (string.IsNullOrWhiteSpace(seriesDataPath))
{
return false;
}
// Check this in order to avoid logging an exception due to directory not existing
if (!Directory.Exists(seriesDataPath))
{
return false;
}
var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath)
var episodeFiles = _fileSystem.GetFilePaths("")
.Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
.Select(Path.GetFileNameWithoutExtension)
.Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase))
@ -118,7 +106,7 @@ namespace MediaBrowser.Providers.TV
if (addNewItems && series.IsMetadataFetcherEnabled(_libraryManager.GetLibraryOptions(series), TvdbSeriesProvider.Current.Name))
{
hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken)
hasNewEpisodes = await AddMissingEpisodes(series, allRecursiveChildren, addMissingEpisodes, "", episodeLookup, cancellationToken)
.ConfigureAwait(false);
}

@ -1,9 +1,8 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
@ -11,23 +10,23 @@ using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using TvDbSharper;
using TvDbSharper.Dto;
namespace MediaBrowser.Providers.TV.TheTVDB
{
public class TvdbEpisodeImageProvider : IRemoteImageProvider
{
private readonly IServerConfigurationManager _config;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly TvDbClient _tvDbClient;
public TvdbEpisodeImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
public TvdbEpisodeImageProvider(IHttpClient httpClient)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
_tvDbClient = new TvDbClient();
_tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey);
}
public string Name => "TheTVDB";
@ -45,113 +44,44 @@ namespace MediaBrowser.Providers.TV.TheTVDB
};
}
public Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
var episode = (Episode)item;
var series = episode.Series;
if (series != null && TvdbSeriesProvider.IsValidSeries(series.ProviderIds))
{
var tvdbId = episode.GetProviderId(MetadataProviders.Tvdb);
// Process images
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, series.ProviderIds);
var episodeResult = await _tvDbClient.Episodes.GetAsync(Convert.ToInt32(tvdbId), cancellationToken);
var nodes = TvdbEpisodeProvider.Current.GetEpisodeXmlNodes(seriesDataPath, episode.GetLookupInfo());
var result = nodes.Select(i => GetImageInfo(i, cancellationToken))
.Where(i => i != null)
.ToList();
return Task.FromResult<IEnumerable<RemoteImageInfo>>(result);
var image = GetImageInfo(episodeResult.Data);
return new List<RemoteImageInfo>
{
image
};
}
return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { });
return new RemoteImageInfo[] { };
}
private RemoteImageInfo GetImageInfo(XmlReader reader, CancellationToken cancellationToken)
private RemoteImageInfo GetImageInfo(EpisodeRecord episode)
{
var height = 225;
var width = 400;
var url = string.Empty;
// Use XmlReader for best performance
using (reader)
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
cancellationToken.ThrowIfCancellationRequested();
switch (reader.Name)
{
case "thumb_width":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
// int.TryParse is local aware, so it can be probamatic, force us culture
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval))
{
width = rval;
}
}
break;
}
case "thumb_height":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
// int.TryParse is local aware, so it can be probamatic, force us culture
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval))
{
height = rval;
}
}
break;
}
case "filename":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
url = TVUtils.BannerUrl + val;
}
break;
}
default:
{
reader.Skip();
break;
}
}
}
else
{
reader.Read();
}
}
}
if (string.IsNullOrEmpty(url))
if (string.IsNullOrEmpty(episode.Filename))
{
return null;
}
return new RemoteImageInfo
{
Width = width,
Height = height,
Width = Convert.ToInt32(episode.ThumbWidth),
Height = Convert.ToInt32(episode.ThumbHeight),
ProviderName = Name,
Url = url,
Url = TVUtils.BannerUrl + episode.Filename,
Type = ImageType.Primary
};
}

File diff suppressed because it is too large Load Diff

@ -80,7 +80,9 @@ namespace MediaBrowser.Providers.TV.TheTVDB
/// <returns>Task.</returns>
public async Task Run(IProgress<double> progress, CancellationToken cancellationToken)
{
var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
return;
var path = "";
//var path = TvdbSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
Directory.CreateDirectory(path);
@ -392,7 +394,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB
Directory.CreateDirectory(seriesDataPath);
return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken);
//return TvdbSeriesProvider.Current.DownloadSeriesZip(id, MetadataProviders.Tvdb.ToString(), null, null, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, cancellationToken);
return Task.CompletedTask;
}
}
}

@ -1,23 +1,19 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Xml;
using TvDbSharper;
using TvDbSharper.Dto;
using RatingType = MediaBrowser.Model.Dto.RatingType;
namespace MediaBrowser.Providers.TV.TheTVDB
{
@ -25,17 +21,14 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IXmlReaderSettingsFactory _xmlSettings;
private readonly TvDbClient _tvDbClient;
public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings)
public TvdbSeasonImageProvider(IHttpClient httpClient)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
_xmlSettings = xmlSettings;
_tvDbClient = new TvDbClient();
_tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey);
}
public string Name => ProviderName;
@ -62,91 +55,68 @@ namespace MediaBrowser.Providers.TV.TheTVDB
var season = (Season)item;
var series = season.Series;
if (series != null && season.IndexNumber.HasValue && TvdbSeriesProvider.IsValidSeries(series.ProviderIds))
if (series == null || !season.IndexNumber.HasValue || !TvdbSeriesProvider.IsValidSeries(series.ProviderIds))
{
var seriesProviderIds = series.ProviderIds;
var seasonNumber = season.IndexNumber.Value;
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesProviderIds, series.Name, series.ProductionYear, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false);
return new RemoteImageInfo[] { };
}
if (!string.IsNullOrEmpty(seriesDataPath))
var seasonNumber = season.IndexNumber.Value;
var language = item.GetPreferredMetadataLanguage();
_tvDbClient.AcceptedLanguage = language;
var remoteImages = new List<RemoteImageInfo>();
var keyTypes = new[] {KeyType.Season, KeyType.Seasonwide, KeyType.Fanart};
// TODO error handling
foreach (KeyType keyType in keyTypes)
{
var imageQuery = new ImagesQuery
{
var path = Path.Combine(seriesDataPath, "banners.xml");
KeyType = keyType,
SubKey = seasonNumber.ToString()
};
var imageResults =
await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(series.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken);
try
{
return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, _xmlSettings, _fileSystem, cancellationToken);
}
catch (FileNotFoundException)
{
// No tvdb data yet. Don't blow up
}
catch (IOException)
{
// No tvdb data yet. Don't blow up
}
}
remoteImages.AddRange(GetImages(imageResults.Data, language));
}
return new RemoteImageInfo[] { };
return remoteImages;
}
internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken)
private static IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
var settings = xmlReaderSettingsFactory.Create(false);
settings.CheckCharacters = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
var list = new List<RemoteImageInfo>();
using (var fileStream = fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
foreach (Image image in images)
{
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
var resolution = image.Resolution.Split('x');
var imageInfo = new RemoteImageInfo
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
cancellationToken.ThrowIfCancellationRequested();
RatingType = RatingType.Score,
CommunityRating = (double?)image.RatingsInfo.Average,
VoteCount = image.RatingsInfo.Count,
Url = TVUtils.BannerUrl + image.FileName,
ProviderName = ProviderName,
// TODO Language = image.LanguageId,
Width = Convert.ToInt32(resolution[0]),
Height = Convert.ToInt32(resolution[1]),
ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail
};
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Banner":
{
if (reader.IsEmptyElement)
{
reader.Read();
continue;
}
using (var subtree = reader.ReadSubtree())
{
AddImage(subtree, list, seasonNumber);
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
}
if (string.Equals(image.KeyType, "season", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Primary;
}
else if (string.Equals(image.KeyType, "seasonwide", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Banner;
}
else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Backdrop;
}
}
list.Add(imageInfo);
}
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
@ -155,6 +125,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
{
return 3;
}
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
@ -162,177 +133,18 @@ namespace MediaBrowser.Providers.TV.TheTVDB
return 2;
}
}
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
.ThenByDescending(i => i.VoteCount ?? 0);
}
private static void AddImage(XmlReader reader, List<RemoteImageInfo> images, int seasonNumber)
{
reader.MoveToContent();
string bannerType = null;
string bannerType2 = null;
string url = null;
int? bannerSeason = null;
int? width = null;
int? height = null;
string language = null;
double? rating = null;
int? voteCount = null;
string thumbnailUrl = null;
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Rating":
{
var val = reader.ReadElementContentAsString() ?? string.Empty;
if (double.TryParse(val, NumberStyles.Any, UsCulture, out var rval))
{
rating = rval;
}
break;
}
case "RatingCount":
{
var val = reader.ReadElementContentAsString() ?? string.Empty;
if (int.TryParse(val, NumberStyles.Integer, UsCulture, out var rval))
{
voteCount = rval;
}
break;
}
case "Language":
{
language = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "ThumbnailPath":
{
thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerType":
{
bannerType = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerType2":
{
bannerType2 = reader.ReadElementContentAsString() ?? string.Empty;
// Sometimes the resolution is stuffed in here
var resolutionParts = bannerType2.Split('x');
if (resolutionParts.Length == 2)
{
if (int.TryParse(resolutionParts[0], NumberStyles.Integer, UsCulture, out var rval))
{
width = rval;
}
if (int.TryParse(resolutionParts[1], NumberStyles.Integer, UsCulture, out rval))
{
height = rval;
}
}
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;
}
}
else
{
reader.Read();
}
}
if (!string.IsNullOrEmpty(url) && bannerSeason.HasValue && bannerSeason.Value == seasonNumber)
{
var imageInfo = new RemoteImageInfo
{
RatingType = RatingType.Score,
CommunityRating = rating,
VoteCount = voteCount,
Url = TVUtils.BannerUrl + url,
ProviderName = ProviderName,
Language = language,
Width = width,
Height = height
};
if (!string.IsNullOrEmpty(thumbnailUrl))
{
imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl;
}
if (string.Equals(bannerType, "season", StringComparison.OrdinalIgnoreCase))
{
if (string.Equals(bannerType2, "season", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Primary;
images.Add(imageInfo);
}
else if (string.Equals(bannerType2, "seasonwide", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Banner;
images.Add(imageInfo);
}
}
else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Backdrop;
images.Add(imageInfo);
}
}
}
public int Order => 0;
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)

@ -1,40 +1,32 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
using MediaBrowser.Model.Xml;
using TvDbSharper;
using TvDbSharper.Dto;
using RatingType = MediaBrowser.Model.Dto.RatingType;
using Series = MediaBrowser.Controller.Entities.TV.Series;
namespace MediaBrowser.Providers.TV.TheTVDB
{
public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
{
private readonly IServerConfigurationManager _config;
private readonly IHttpClient _httpClient;
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
private readonly IFileSystem _fileSystem;
private readonly IXmlReaderSettingsFactory _xmlReaderSettingsFactory;
private readonly TvDbClient _tvDbClient = new TvDbClient();
public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory)
public TvdbSeriesImageProvider(IHttpClient httpClient)
{
_config = config;
_httpClient = httpClient;
_fileSystem = fileSystem;
_xmlReaderSettingsFactory = xmlReaderSettingsFactory;
_tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey);
}
public string Name => ProviderName;
@ -58,273 +50,92 @@ namespace MediaBrowser.Providers.TV.TheTVDB
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
if (TvdbSeriesProvider.IsValidSeries(item.ProviderIds))
if (!TvdbSeriesProvider.IsValidSeries(item.ProviderIds))
{
var language = item.GetPreferredMetadataLanguage();
var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(item.ProviderIds, item.Name, item.ProductionYear, language, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(seriesDataPath))
{
return new RemoteImageInfo[] { };
}
var path = Path.Combine(seriesDataPath, "banners.xml");
try
{
return GetImages(path, language, cancellationToken);
}
catch (FileNotFoundException)
{
// No tvdb data yet. Don't blow up
}
catch (IOException)
{
// No tvdb data yet. Don't blow up
}
return new RemoteImageInfo[] { };
}
return new RemoteImageInfo[] { };
}
private IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken)
{
var settings = _xmlReaderSettingsFactory.Create(false);
settings.CheckCharacters = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
var list = new List<RemoteImageInfo>();
using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
var language = item.GetPreferredMetadataLanguage();
_tvDbClient.AcceptedLanguage = language;
var remoteImages = new List<RemoteImageInfo>();
var keyTypes = new[] {KeyType.Poster, KeyType.Series, KeyType.Fanart};
// TODO error handling
foreach (KeyType keyType in keyTypes)
{
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
var imageQuery = new ImagesQuery
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
cancellationToken.ThrowIfCancellationRequested();
KeyType = keyType
};
var imageResults =
await _tvDbClient.Series.GetImagesAsync(Convert.ToInt32(item.GetProviderId(MetadataProviders.Tvdb)), imageQuery, cancellationToken);
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Banner":
{
if (reader.IsEmptyElement)
{
reader.Read();
continue;
}
using (var subtree = reader.ReadSubtree())
{
AddImage(subtree, list);
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
}
}
remoteImages.AddRange(GetImages(imageResults.Data, language));
}
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
{
return 3;
}
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
{
return 2;
}
}
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
.ThenByDescending(i => i.VoteCount ?? 0);
return remoteImages;
}
private void AddImage(XmlReader reader, List<RemoteImageInfo> images)
private IEnumerable<RemoteImageInfo> GetImages(Image[] images, string preferredLanguage)
{
reader.MoveToContent();
string bannerType = null;
string url = null;
int? bannerSeason = null;
int? width = null;
int? height = null;
string language = null;
double? rating = null;
int? voteCount = null;
string thumbnailUrl = null;
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Rating":
{
var val = reader.ReadElementContentAsString() ?? string.Empty;
if (double.TryParse(val, NumberStyles.Any, _usCulture, out var rval))
{
rating = rval;
}
break;
}
case "RatingCount":
{
var val = reader.ReadElementContentAsString() ?? string.Empty;
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval))
{
voteCount = rval;
}
break;
}
case "Language":
{
language = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "ThumbnailPath":
{
thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerType":
{
bannerType = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerPath":
{
url = reader.ReadElementContentAsString() ?? string.Empty;
break;
}
case "BannerType2":
{
var bannerType2 = reader.ReadElementContentAsString() ?? string.Empty;
// Sometimes the resolution is stuffed in here
var resolutionParts = bannerType2.Split('x');
if (resolutionParts.Length == 2)
{
if (int.TryParse(resolutionParts[0], NumberStyles.Integer, _usCulture, out var rval))
{
width = rval;
}
if (int.TryParse(resolutionParts[1], NumberStyles.Integer, _usCulture, out rval))
{
height = rval;
}
}
break;
}
case "Season":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
bannerSeason = int.Parse(val);
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
var list = new List<RemoteImageInfo>();
if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue)
foreach (Image image in images)
{
var resolution = image.Resolution.Split('x');
var imageInfo = new RemoteImageInfo
{
RatingType = RatingType.Score,
CommunityRating = rating,
VoteCount = voteCount,
Url = TVUtils.BannerUrl + url,
CommunityRating = (double?)image.RatingsInfo.Average,
VoteCount = image.RatingsInfo.Count,
Url = TVUtils.BannerUrl + image.FileName,
ProviderName = Name,
Language = language,
Width = width,
Height = height
// TODO Language = image.LanguageId,
Width = Convert.ToInt32(resolution[0]),
Height = Convert.ToInt32(resolution[1]),
ThumbnailUrl = TVUtils.BannerUrl + image.Thumbnail
};
if (!string.IsNullOrEmpty(thumbnailUrl))
{
imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl;
}
if (string.Equals(bannerType, "poster", StringComparison.OrdinalIgnoreCase))
if (string.Equals(image.KeyType, "poster", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Primary;
images.Add(imageInfo);
}
else if (string.Equals(bannerType, "series", StringComparison.OrdinalIgnoreCase))
else if (string.Equals(image.KeyType, "series", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Banner;
images.Add(imageInfo);
}
else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase))
else if (string.Equals(image.KeyType, "fanart", StringComparison.OrdinalIgnoreCase))
{
imageInfo.Type = ImageType.Backdrop;
images.Add(imageInfo);
}
list.Add(imageInfo);
}
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
return list.OrderByDescending(i =>
{
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
{
return 3;
}
if (!isLanguageEn)
{
if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase))
{
return 2;
}
}
if (string.IsNullOrEmpty(i.Language))
{
return isLanguageEn ? 3 : 2;
}
return 0;
})
.ThenByDescending(i => i.CommunityRating ?? 0)
.ThenByDescending(i => i.VoteCount ?? 0);
}
public int Order => 0;

@ -30,7 +30,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB
public class TvdbSeriesProvider : IRemoteMetadataProvider<Series, SeriesInfo>, IHasOrder
{
internal static TvdbSeriesProvider Current { get; private set; }
private readonly IZipClient _zipClient;
private readonly IHttpClient _httpClient;
private readonly IFileSystem _fileSystem;
private readonly IXmlReaderSettingsFactory _xmlSettings;
@ -41,9 +40,8 @@ namespace MediaBrowser.Providers.TV.TheTVDB
private readonly ILocalizationManager _localizationManager;
private readonly TvDbClient _tvDbClient;
public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
public TvdbSeriesProvider(IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager)
{
_zipClient = zipClient;
_httpClient = httpClient;
_fileSystem = fileSystem;
_config = config;
@ -53,6 +51,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
_localizationManager = localizationManager;
Current = this;
_tvDbClient = new TvDbClient();
_tvDbClient.Authentication.AuthenticateAsync(TVUtils.TvdbApiKey);
}
private string NormalizeLanguage(string language)
@ -93,8 +92,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB
public async Task<MetadataResult<Series>> GetMetadata(SeriesInfo itemId, CancellationToken cancellationToken)
{
var result = new MetadataResult<Series>();
result.QueriedById = true;
var result = new MetadataResult<Series>
{
QueriedById = true
};
if (!IsValidSeries(itemId.ProviderIds))
{
@ -106,13 +107,6 @@ namespace MediaBrowser.Providers.TV.TheTVDB
if (IsValidSeries(itemId.ProviderIds))
{
var seriesDataPath = await EnsureSeriesInfo(itemId.ProviderIds, itemId.Name, itemId.Year, itemId.MetadataLanguage, cancellationToken).ConfigureAwait(false);
if (string.IsNullOrEmpty(seriesDataPath))
{
return result;
}
result.Item = new Series();
result.HasMetadata = true;
@ -122,156 +116,43 @@ namespace MediaBrowser.Providers.TV.TheTVDB
return result;
}
/// <summary>
/// Fetches the series data.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="metadataLanguage">The metadata language.</param>
/// <param name="seriesProviderIds">The series provider ids.</param>
/// <param name="cancellationToken">The cancellation token.</param>
/// <returns>Task{System.Boolean}.</returns>
private void FetchSeriesData(MetadataResult<Series> result, string metadataLanguage, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
private async Task FetchSeriesData(MetadataResult<Series> result, string metadataLanguage, Dictionary<string, string> seriesProviderIds, CancellationToken cancellationToken)
{
_tvDbClient.AcceptedLanguage = NormalizeLanguage(metadataLanguage);
var series = result.Item;
TvDbResponse<SeriesSearchResult[]> searchResult;
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(id))
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out var tvdbId) && !string.IsNullOrEmpty(tvdbId))
{
series.SetProviderId(MetadataProviders.Tvdb, tvdbId);
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(id))
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out var imdbId) && !string.IsNullOrEmpty(imdbId))
{
series.SetProviderId(MetadataProviders.Imdb, imdbId);
tvdbId = GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken).Result.ToString();
tvdbId = await GetSeriesByRemoteId(imdbId, MetadataProviders.Imdb.ToString(), metadataLanguage, cancellationToken);
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(id))
if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out var zap2It) && !string.IsNullOrEmpty(zap2It))
{
series.SetProviderId(MetadataProviders.Zap2It, zap2It);
tvdbId = GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken).Result.ToString();
tvdbId = await GetSeriesByRemoteId(zap2It, MetadataProviders.Zap2It.ToString(), metadataLanguage, cancellationToken);
}
var seriesResult = _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken).Result;
// var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
//
// var seriesXmlPath = GetSeriesXmlPath(seriesProviderIds, metadataLanguage);
// var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml");
// TODO call this function elsewhere?
var seriesResult = await _tvDbClient.Series.GetAsync(Convert.ToInt32(tvdbId), cancellationToken);
FetchSeriesInfo(result, searchResult, cancellationToken);
// TODO error handling
MapSeriesToResult(result, seriesResult.Data, cancellationToken);
cancellationToken.ThrowIfCancellationRequested();
result.ResetPeople();
FetchActors(result, actorsXmlPath);
var actorsResult = await _tvDbClient.Series.GetActorsAsync(Convert.ToInt32(tvdbId), cancellationToken);
MapActorsToResult(result, actorsResult.Data);
}
/// <summary>
/// Downloads the series zip.
/// </summary>
internal async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, CancellationToken cancellationToken)
{
try
{
await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, preferredMetadataLanguage, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
return;
}
catch (HttpException ex)
{
if (!ex.StatusCode.HasValue || ex.StatusCode.Value != HttpStatusCode.NotFound)
{
throw;
}
}
if (!string.Equals(preferredMetadataLanguage, "en", StringComparison.OrdinalIgnoreCase))
{
await DownloadSeriesZip(seriesId, idType, seriesName, seriesYear, seriesDataPath, lastTvDbUpdateTime, "en", preferredMetadataLanguage, cancellationToken).ConfigureAwait(false);
}
}
private async Task DownloadSeriesZip(string seriesId, string idType, string seriesName, int? seriesYear, string seriesDataPath, long? lastTvDbUpdateTime, string preferredMetadataLanguage, string saveAsMetadataLanguage, CancellationToken cancellationToken)
{
if (string.IsNullOrWhiteSpace(seriesId))
{
throw new ArgumentNullException(nameof(seriesId));
}
if (!string.Equals(idType, "tvdb", StringComparison.OrdinalIgnoreCase))
{
seriesId = (await GetSeriesByRemoteId(seriesId, idType, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false)).ToString();
}
// If searching by remote id came up empty, then do a regular search
if (string.IsNullOrWhiteSpace(seriesId) && !string.IsNullOrWhiteSpace(seriesName))
{
var searchInfo = new SeriesInfo
{
Name = seriesName,
Year = seriesYear,
MetadataLanguage = preferredMetadataLanguage
};
var results = await GetSearchResults(searchInfo, cancellationToken).ConfigureAwait(false);
var result = results.FirstOrDefault();
if (result != null)
{
seriesId = result.GetProviderId(MetadataProviders.Tvdb);
}
}
if (string.IsNullOrWhiteSpace(seriesId))
{
throw new ArgumentNullException(nameof(seriesId));
}
var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, NormalizeLanguage(preferredMetadataLanguage));
using (var response = await _httpClient.SendAsync(new HttpRequestOptions
{
Url = url,
CancellationToken = cancellationToken,
BufferContent = false
}, "GET").ConfigureAwait(false))
{
using (var zipStream = response.Content)
{
// Delete existing files
DeleteXmlFiles(seriesDataPath);
// Copy to memory stream because we need a seekable stream
using (var ms = new MemoryStream())
{
await zipStream.CopyToAsync(ms).ConfigureAwait(false);
ms.Position = 0;
_zipClient.ExtractAllFromZip(ms, seriesDataPath, true);
}
}
}
// Sanitize all files, except for extracted episode files
foreach (var file in _fileSystem.GetFilePaths(seriesDataPath, true).ToList()
.Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase))
.Where(i => !Path.GetFileName(i).StartsWith("episode-", StringComparison.OrdinalIgnoreCase)))
{
await SanitizeXmlFile(file).ConfigureAwait(false);
}
var downloadLangaugeXmlFile = Path.Combine(seriesDataPath, NormalizeLanguage(preferredMetadataLanguage) + ".xml");
var saveAsLanguageXmlFile = Path.Combine(seriesDataPath, saveAsMetadataLanguage + ".xml");
if (!string.Equals(downloadLangaugeXmlFile, saveAsLanguageXmlFile, StringComparison.OrdinalIgnoreCase))
{
File.Copy(downloadLangaugeXmlFile, saveAsLanguageXmlFile, true);
}
await ExtractEpisodes(seriesDataPath, downloadLangaugeXmlFile, lastTvDbUpdateTime).ConfigureAwait(false);
}
private async Task<int> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken)
private async Task<string> GetSeriesByRemoteId(string id, string idType, string language, CancellationToken cancellationToken)
{
_tvDbClient.AcceptedLanguage = NormalizeLanguage(language);
TvDbResponse<SeriesSearchResult[]> result;
@ -285,7 +166,7 @@ namespace MediaBrowser.Providers.TV.TheTVDB
result = await _tvDbClient.Search.SearchSeriesByImdbIdAsync(id, cancellationToken);
}
return result.Data.First().Id;
return result.Data.First().Id.ToString();
}
internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds)
@ -395,10 +276,10 @@ namespace MediaBrowser.Providers.TV.TheTVDB
Name = tvdbTitles.FirstOrDefault(),
ProductionYear = firstAired.Year,
SearchProviderName = Name,
ImageUrl = seriesSearchResult.Banner
ImageUrl = TVUtils.BannerUrl + seriesSearchResult.Banner
};
// TODO requires another query
// TODO requires another query, is it worth it?
// remoteSearchResult.SetProviderId(MetadataProviders.Imdb, seriesSearchResult.Id);
remoteSearchResult.SetProviderId(MetadataProviders.Tvdb, seriesSearchResult.Id.ToString());
list.Add(new Tuple<List<string>, RemoteSearchResult>(tvdbTitles, remoteSearchResult));
@ -468,27 +349,26 @@ namespace MediaBrowser.Providers.TV.TheTVDB
return name.Trim();
}
private void FetchSeriesInfo(MetadataResult<Series> result, TvDbResponse<TvDbSharper.Dto.Series> seriesResponse, CancellationToken cancellationToken)
private static void MapSeriesToResult(MetadataResult<Series> result, TvDbSharper.Dto.Series tvdbSeries, CancellationToken cancellationToken)
{
var episodeAirDates = new List<DateTime>();
var series = result.Item;
Series item = result.Item;
series.SetProviderId(MetadataProviders.Tvdb, seriesResponse.Data.Id.ToString());
series.Name = seriesResponse.Data.SeriesName;
series.Overview = (seriesResponse.Data.Overview ?? string.Empty).Trim();
Series series = result.Item;
series.SetProviderId(MetadataProviders.Tvdb, tvdbSeries.Id.ToString());
series.Name = tvdbSeries.SeriesName;
series.Overview = (tvdbSeries.Overview ?? string.Empty).Trim();
// TODO result.ResultLanguage = (seriesResponse.Data. ?? string.Empty).Trim();
series.AirDays = TVUtils.GetAirDays(seriesResponse.Data.AirsDayOfWeek);
series.AirTime = seriesResponse.Data.AirsTime;
series.AirDays = TVUtils.GetAirDays(tvdbSeries.AirsDayOfWeek);
series.AirTime = tvdbSeries.AirsTime;
series.CommunityRating = (float?)seriesResponse.Data.SiteRating;
series.SetProviderId(MetadataProviders.Imdb, seriesResponse.Data.ImdbId);
series.SetProviderId(MetadataProviders.Zap2It, seriesResponse.Data.Zap2itId);
if (Enum.TryParse(seriesResponse.Data.Status, true, out SeriesStatus seriesStatus))
series.CommunityRating = (float?)tvdbSeries.SiteRating;
series.SetProviderId(MetadataProviders.Imdb, tvdbSeries.ImdbId);
series.SetProviderId(MetadataProviders.Zap2It, tvdbSeries.Zap2itId);
if (Enum.TryParse(tvdbSeries.Status, true, out SeriesStatus seriesStatus))
{
series.Status = seriesStatus;
}
if (DateTime.TryParse(seriesResponse.Data.FirstAired, out var date))
if (DateTime.TryParse(tvdbSeries.FirstAired, out var date))
{
date = date.ToUniversalTime();
@ -496,697 +376,40 @@ namespace MediaBrowser.Providers.TV.TheTVDB
series.ProductionYear = date.Year;
}
series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(seriesResponse.Data.Runtime)).Ticks;
if (!string.IsNullOrWhiteSpace(val))
series.RunTimeTicks = TimeSpan.FromMinutes(Convert.ToDouble(tvdbSeries.Runtime)).Ticks;
foreach (var genre in tvdbSeries.Genre)
{
var vals = val
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(i => i.Trim())
.Where(i => !string.IsNullOrWhiteSpace(i))
.ToList();
if (vals.Count > 0)
{
item.Genres = Array.Empty<string>();
foreach (var genre in vals)
{
item.AddGenre(genre);
}
}
series.AddGenre(genre);
}
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
var vals = val
.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
.Select(i => i.Trim())
.Where(i => !string.IsNullOrWhiteSpace(i))
.ToList();
if (vals.Count > 0)
{
item.SetStudios(vals);
}
}
// using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
// {
// using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
// {
// // Use XmlReader for best performance
// using (var reader = XmlReader.Create(streamReader, settings))
// {
// reader.MoveToContent();
// reader.Read();
//
// // Loop through each element
// while (!reader.EOF && reader.ReadState == ReadState.Interactive)
// {
// cancellationToken.ThrowIfCancellationRequested();
//
// if (reader.NodeType == XmlNodeType.Element)
// {
// switch (reader.Name)
// {
// case "Series":
// {
// if (reader.IsEmptyElement)
// {
// reader.Read();
// continue;
// }
// using (var subtree = reader.ReadSubtree())
// {
// FetchDataFromSeriesNode(result, subtree, cancellationToken);
// }
// break;
// }
//
// case "Episode":
// {
// if (reader.IsEmptyElement)
// {
// reader.Read();
// continue;
// }
// using (var subtree = reader.ReadSubtree())
// {
// var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken);
//
// if (date.HasValue)
// {
// episiodeAirDates.Add(date.Value);
// }
// }
// break;
// }
//
// default:
// reader.Skip();
// break;
// }
// }
// else
// {
// reader.Read();
// }
// }
// }
// }
// }
// TODO is network == studio?
series.AddStudio(tvdbSeries.Network);
// TODO is this necessary?
if (result.Item.Status.HasValue && result.Item.Status.Value == SeriesStatus.Ended && episodeAirDates.Count > 0)
{
result.Item.EndDate = episodeAirDates.Max();
}
}
private DateTime? GetFirstAiredDateFromEpisodeNode(XmlReader reader, CancellationToken cancellationToken)
{
DateTime? airDate = null;
int? seasonNumber = null;
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
cancellationToken.ThrowIfCancellationRequested();
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "FirstAired":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (DateTime.TryParse(val, out var date))
{
airDate = date.ToUniversalTime();
}
}
break;
}
case "SeasonNumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
// int.TryParse is local aware, so it can be probamatic, force us culture
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval))
{
seasonNumber = rval;
}
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
if (seasonNumber.HasValue && seasonNumber.Value != 0)
{
return airDate;
}
return null;
}
/// <summary>
/// Fetches the actors.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="actorsXmlPath">The actors XML path.</param>
private void FetchActors(MetadataResult<Series> result, string actorsXmlPath)
{
var settings = _xmlSettings.Create(false);
settings.CheckCharacters = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
using (var fileStream = _fileSystem.GetFileStream(actorsXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
{
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Actor":
{
if (reader.IsEmptyElement)
{
reader.Read();
continue;
}
using (var subtree = reader.ReadSubtree())
{
FetchDataFromActorNode(result, subtree);
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
}
}
}
}
/// <summary>
/// Fetches the data from actor node.
/// </summary>
/// <param name="result">The result.</param>
/// <param name="reader">The reader.</param>
private void FetchDataFromActorNode(MetadataResult<Series> result, XmlReader reader)
{
reader.MoveToContent();
var personInfo = new PersonInfo();
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Name":
{
personInfo.Name = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
break;
}
case "Role":
{
personInfo.Role = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
break;
}
case "id":
{
reader.Skip();
break;
}
case "Image":
{
var url = (reader.ReadElementContentAsString() ?? string.Empty).Trim();
if (!string.IsNullOrWhiteSpace(url))
{
personInfo.ImageUrl = TVUtils.BannerUrl + url;
}
break;
}
case "SortOrder":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
// int.TryParse is local aware, so it can be probamatic, force us culture
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var rval))
{
personInfo.SortOrder = rval;
}
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
personInfo.Type = PersonType.Actor;
if (!string.IsNullOrWhiteSpace(personInfo.Name))
{
result.AddPerson(personInfo);
}
}
private void FetchDataFromSeriesNode(MetadataResult<Series> result, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
}
/// <summary>
/// Extracts info for each episode into invididual xml files so that they can be easily accessed without having to step through the entire series xml
/// </summary>
/// <param name="seriesDataPath">The series data path.</param>
/// <param name="xmlFile">The XML file.</param>
/// <param name="lastTvDbUpdateTime">The last tv db update time.</param>
/// <returns>Task.</returns>
private async Task ExtractEpisodes(string seriesDataPath, string xmlFile, long? lastTvDbUpdateTime)
private static void MapActorsToResult(MetadataResult<Series> result, IEnumerable<Actor> actors)
{
var settings = _xmlSettings.Create(false);
settings.CheckCharacters = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
foreach (Actor actor in actors)
{
using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Episode":
{
var outerXml = reader.ReadOuterXml();
await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false);
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
}
}
}
}
private async Task SaveEpsiodeXml(string seriesDataPath, string xml, long? lastTvDbUpdateTime)
{
var settings = _xmlSettings.Create(false);
settings.CheckCharacters = false;
settings.IgnoreProcessingInstructions = true;
settings.IgnoreComments = true;
var seasonNumber = -1;
var episodeNumber = -1;
var absoluteNumber = -1;
var lastUpdateString = string.Empty;
var dvdSeasonNumber = -1;
var dvdEpisodeNumber = -1.0;
using (var streamReader = new StringReader(xml))
{
// Use XmlReader for best performance
using (var reader = XmlReader.Create(streamReader, settings))
{
reader.MoveToContent();
reader.Read();
// Loop through each element
while (!reader.EOF && reader.ReadState == ReadState.Interactive)
{
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "lastupdated":
{
lastUpdateString = reader.ReadElementContentAsString();
break;
}
case "EpisodeNumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num))
{
episodeNumber = num;
}
}
break;
}
case "Combined_episodenumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
{
dvdEpisodeNumber = num;
}
}
break;
}
case "Combined_season":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
{
dvdSeasonNumber = Convert.ToInt32(num);
}
}
break;
}
case "absolute_number":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num))
{
absoluteNumber = num;
}
}
break;
}
case "SeasonNumber":
{
var val = reader.ReadElementContentAsString();
if (!string.IsNullOrWhiteSpace(val))
{
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out var num))
{
seasonNumber = num;
}
}
break;
}
default:
reader.Skip();
break;
}
}
else
{
reader.Read();
}
}
}
}
var hasEpisodeChanged = true;
if (!string.IsNullOrWhiteSpace(lastUpdateString) && lastTvDbUpdateTime.HasValue)
{
if (long.TryParse(lastUpdateString, NumberStyles.Any, _usCulture, out var num))
{
hasEpisodeChanged = num >= lastTvDbUpdateTime.Value;
}
}
var file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber, episodeNumber));
// Only save the file if not already there, or if the episode has changed
if (hasEpisodeChanged || !File.Exists(file))
{
using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Async = true
}))
{
await writer.WriteRawAsync(xml).ConfigureAwait(false);
}
}
}
if (absoluteNumber != -1)
{
file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", absoluteNumber));
// Only save the file if not already there, or if the episode has changed
if (hasEpisodeChanged || !File.Exists(file))
{
using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Async = true
}))
{
await writer.WriteRawAsync(xml).ConfigureAwait(false);
}
}
}
}
if (dvdSeasonNumber != -1 && dvdEpisodeNumber != -1 && (dvdSeasonNumber != seasonNumber || dvdEpisodeNumber != episodeNumber))
{
file = Path.Combine(seriesDataPath, string.Format("episode-dvd-{0}-{1}.xml", dvdSeasonNumber, dvdEpisodeNumber));
// Only save the file if not already there, or if the episode has changed
if (hasEpisodeChanged || !File.Exists(file))
var personInfo = new PersonInfo
{
using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true))
{
using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Async = true
}))
{
await writer.WriteRawAsync(xml).ConfigureAwait(false);
}
}
}
}
}
/// <summary>
/// Gets the series data path.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <param name="seriesProviderIds">The series provider ids.</param>
/// <returns>System.String.</returns>
internal static string GetSeriesDataPath(IApplicationPaths appPaths, Dictionary<string, string> seriesProviderIds)
{
if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out string seriesId) && !string.IsNullOrEmpty(seriesId))
{
var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
return seriesDataPath;
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId))
{
var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
return seriesDataPath;
}
if (seriesProviderIds.TryGetValue(MetadataProviders.Zap2It.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId))
{
var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
return seriesDataPath;
}
return null;
}
public string GetSeriesXmlPath(Dictionary<string, string> seriesProviderIds, string language)
{
var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds);
var seriesXmlFilename = language.ToLowerInvariant() + ".xml";
return Path.Combine(seriesDataPath, seriesXmlFilename);
}
/// <summary>
/// Gets the series data path.
/// </summary>
/// <param name="appPaths">The app paths.</param>
/// <returns>System.String.</returns>
internal static string GetSeriesDataPath(IApplicationPaths appPaths)
{
var dataPath = Path.Combine(appPaths.CachePath, "tvdb");
return dataPath;
}
private void DeleteXmlFiles(string path)
{
try
{
foreach (var file in _fileSystem.GetFilePaths(path, true)
.ToList())
{
_fileSystem.DeleteFile(file);
}
}
catch (IOException)
{
// No biggie
}
}
/// <summary>
/// Sanitizes the XML file.
/// </summary>
/// <param name="file">The file.</param>
/// <returns>Task.</returns>
private async Task SanitizeXmlFile(string file)
{
string validXml;
using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true))
{
using (var reader = new StreamReader(fileStream))
{
var xml = await reader.ReadToEndAsync().ConfigureAwait(false);
validXml = StripInvalidXmlCharacters(xml);
}
}
using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true))
{
using (var writer = new StreamWriter(fileStream))
{
await writer.WriteAsync(validXml).ConfigureAwait(false);
}
}
}
/// <summary>
/// Strips the invalid XML characters.
/// </summary>
/// <param name="inString">The in string.</param>
/// <returns>System.String.</returns>
public static string StripInvalidXmlCharacters(string inString)
{
if (inString == null) return null;
var sbOutput = new StringBuilder();
char ch;
Type = PersonType.Actor,
Name = (actor.Name ?? string.Empty).Trim(),
Role = actor.Role,
ImageUrl = actor.Image,
SortOrder = actor.SortOrder
};
for (int i = 0; i < inString.Length; i++)
{
ch = inString[i];
if ((ch >= 0x0020 && ch <= 0xD7FF) ||
(ch >= 0xE000 && ch <= 0xFFFD) ||
ch == 0x0009 ||
ch == 0x000A ||
ch == 0x000D)
if (!string.IsNullOrWhiteSpace(personInfo.Name))
{
sbOutput.Append(ch);
result.AddPerson(personInfo);
}
}
return sbOutput.ToString();
}
public string Name => "TheTVDB";

Loading…
Cancel
Save