Răsfoiți Sursa

Some of it works???

Claus Vium 6 ani în urmă
părinte
comite
c2202be0f8

+ 1 - 1
MediaBrowser.Controller/Library/TVUtils.cs

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

+ 2 - 15
MediaBrowser.Providers/People/TvdbPersonImageProvider.cs

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

+ 4 - 16
MediaBrowser.Providers/TV/MissingEpisodeProvider.cs

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

+ 21 - 91
MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs

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

+ 872 - 898
MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs

@@ -1,898 +1,872 @@
-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.Providers;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
-using MediaBrowser.Model.Providers;
-using MediaBrowser.Model.Xml;
-using Microsoft.Extensions.Logging;
-
-namespace MediaBrowser.Providers.TV.TheTVDB
-{
-
-    /// <summary>
-    /// Class RemoteEpisodeProvider
-    /// </summary>
-    class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
-    {
-        private static readonly string FullIdKey = MetadataProviders.Tvdb + "-Full";
-
-        internal static TvdbEpisodeProvider Current;
-        private readonly IFileSystem _fileSystem;
-        private readonly IServerConfigurationManager _config;
-        private readonly IHttpClient _httpClient;
-        private readonly ILogger _logger;
-        private readonly IXmlReaderSettingsFactory _xmlSettings;
-
-        public TvdbEpisodeProvider(IFileSystem fileSystem, IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IXmlReaderSettingsFactory xmlSettings)
-        {
-            _fileSystem = fileSystem;
-            _config = config;
-            _httpClient = httpClient;
-            _logger = logger;
-            _xmlSettings = xmlSettings;
-            Current = this;
-        }
-
-        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
-        {
-            var list = new List<RemoteSearchResult>();
-
-            // The search query must either provide an episode number or date
-            if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue)
-            {
-                return Task.FromResult((IEnumerable<RemoteSearchResult>)list);
-            }
-
-            if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds))
-            {
-                var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds);
-
-                try
-                {
-                    var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
-
-                    if (metadataResult.HasMetadata)
-                    {
-                        var item = metadataResult.Item;
-
-                        list.Add(new RemoteSearchResult
-                        {
-                            IndexNumber = item.IndexNumber,
-                            Name = item.Name,
-                            ParentIndexNumber = item.ParentIndexNumber,
-                            PremiereDate = item.PremiereDate,
-                            ProductionYear = item.ProductionYear,
-                            ProviderIds = item.ProviderIds,
-                            SearchProviderName = Name,
-                            IndexNumberEnd = item.IndexNumberEnd
-                        });
-                    }
-                }
-                catch (FileNotFoundException)
-                {
-                    // Don't fail the provider because this will just keep on going and going.
-                }
-                catch (IOException)
-                {
-                    // Don't fail the provider because this will just keep on going and going.
-                }
-            }
-
-            return Task.FromResult((IEnumerable<RemoteSearchResult>)list);
-        }
-
-        public string Name => "TheTVDB";
-
-        public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken)
-        {
-            var result = new MetadataResult<Episode>();
-            result.QueriedById = true;
-
-            if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
-                (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
-            {
-                var seriesDataPath = await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, null, null, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false);
-
-                if (string.IsNullOrEmpty(seriesDataPath))
-                {
-                    return result;
-                }
-
-                try
-                {
-                    result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
-                }
-                catch (FileNotFoundException)
-                {
-                    // Don't fail the provider because this will just keep on going and going.
-                }
-                catch (IOException)
-                {
-                    // Don't fail the provider because this will just keep on going and going.
-                }
-            }
-            else
-            {
-                _logger.LogDebug("No series identity found for {0}", searchInfo.Name);
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// Gets the episode XML files.
-        /// </summary>
-        /// <param name="seriesDataPath">The series data path.</param>
-        /// <param name="searchInfo">The search information.</param>
-        /// <returns>List{FileInfo}.</returns>
-        internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo)
-        {
-            var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage);
-
-            try
-            {
-                return GetXmlNodes(seriesXmlPath, searchInfo);
-            }
-            catch (FileNotFoundException)
-            {
-                return new List<XmlReader>();
-            }
-            catch (IOException)
-            {
-                return new List<XmlReader>();
-            }
-        }
-
-        /// <summary>
-        /// Fetches the episode data.
-        /// </summary>
-        /// <param name="id">The identifier.</param>
-        /// <param name="seriesDataPath">The series data path.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task{System.Boolean}.</returns>
-        private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken)
-        {
-            var result = new MetadataResult<Episode>()
-            {
-                Item = new Episode
-                {
-                    IndexNumber = id.IndexNumber,
-                    ParentIndexNumber = id.ParentIndexNumber,
-                    IndexNumberEnd = id.IndexNumberEnd
-                }
-            };
-
-            var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id);
-
-            if (xmlNodes.Count > 0)
-            {
-                FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken);
-
-                result.HasMetadata = true;
-            }
-
-            foreach (var node in xmlNodes.Skip(1))
-            {
-                FetchAdditionalPartInfo(result, node, cancellationToken);
-            }
-
-            return result;
-        }
-
-        private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo)
-        {
-            var list = new List<XmlReader>();
-
-            if (searchInfo.IndexNumber.HasValue)
-            {
-                var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile));
-
-                list = files.Select(GetXmlReader).ToList();
-            }
-
-            if (list.Count == 0 && searchInfo.PremiereDate.HasValue)
-            {
-                list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value);
-            }
-
-            return list;
-        }
-
-        private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
-        {
-            if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase))
-            {
-                return string.Format("episode-abs-{0}.xml", episodeNumber);
-            }
-            else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
-            {
-                return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
-            }
-            else
-            {
-                return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
-            }
-        }
-
-        private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
-        {
-            var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber));
-            var fileInfo = _fileSystem.GetFileInfo(file);
-
-            if (fileInfo.Exists)
-            {
-                return fileInfo;
-            }
-
-            if (!seasonNumber.HasValue)
-            {
-                return fileInfo;
-            }
-
-            // revert to aired order
-            if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
-            {
-                file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber));
-                return _fileSystem.GetFileInfo(file);
-            }
-
-            return fileInfo;
-        }
-
-        private List<FileSystemMetadata> GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
-        {
-            var files = new List<FileSystemMetadata>();
-
-            if (episodeNumber == null)
-            {
-                return files;
-            }
-
-            if (!seasonNumber.HasValue)
-            {
-                seriesDisplayOrder = "absolute";
-            }
-
-            var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
-
-            if (fileInfo.Exists)
-            {
-                files.Add(fileInfo);
-            }
-
-            var end = endingEpisodeNumber ?? episodeNumber;
-            episodeNumber++;
-
-            while (episodeNumber <= end)
-            {
-                fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
-
-                if (fileInfo.Exists)
-                {
-                    files.Add(fileInfo);
-                }
-                else
-                {
-                    break;
-                }
-
-                episodeNumber++;
-            }
-
-            return files;
-        }
-
-        private XmlReader GetXmlReader(FileSystemMetadata xmlFile)
-        {
-            return GetXmlReader(File.ReadAllText(xmlFile.FullName, Encoding.UTF8));
-        }
-
-        private XmlReader GetXmlReader(string xml)
-        {
-            var streamReader = new StringReader(xml);
-
-            var settings = _xmlSettings.Create(false);
-
-            settings.CheckCharacters = false;
-            settings.IgnoreProcessingInstructions = true;
-            settings.IgnoreComments = true;
-
-            return XmlReader.Create(streamReader, settings);
-        }
-
-        private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate)
-        {
-            var list = new List<XmlReader>();
-
-            using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
-            {
-                using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
-                {
-                    // Use XmlReader for best performance
-
-                    var settings = _xmlSettings.Create(false);
-
-                    settings.CheckCharacters = false;
-                    settings.IgnoreProcessingInstructions = true;
-                    settings.IgnoreComments = true;
-
-                    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();
-
-                                            var airDate = GetEpisodeAirDate(outerXml);
-
-                                            if (airDate.HasValue && premiereDate.Date == airDate.Value.Date)
-                                            {
-                                                list.Add(GetXmlReader(outerXml));
-                                                return list;
-                                            }
-
-                                            break;
-                                        }
-
-                                    default:
-                                        reader.Skip();
-                                        break;
-                                }
-                            }
-                            else
-                            {
-                                reader.Read();
-                            }
-                        }
-                    }
-                }
-            }
-
-            return list;
-        }
-
-        private DateTime? GetEpisodeAirDate(string xml)
-        {
-            using (var streamReader = new StringReader(xml))
-            {
-                var settings = _xmlSettings.Create(false);
-
-                settings.CheckCharacters = false;
-                settings.IgnoreProcessingInstructions = true;
-                settings.IgnoreComments = true;
-
-                // 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 "FirstAired":
-                                    {
-                                        var val = reader.ReadElementContentAsString();
-
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            if (DateTime.TryParse(val, out var date))
-                                            {
-                                                date = date.ToUniversalTime();
-
-                                                return date;
-                                            }
-                                        }
-
-                                        break;
-                                    }
-
-                                default:
-                                    reader.Skip();
-                                    break;
-                            }
-                        }
-                        else
-                        {
-                            reader.Read();
-                        }
-                    }
-                }
-            }
-            return null;
-        }
-
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-
-        private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken)
-        {
-            var item = result.Item;
-
-            int? episodeNumber = null;
-            int? seasonNumber = null;
-            int? combinedEpisodeNumber = null;
-            int? combinedSeasonNumber = null;
-
-            // Use XmlReader for best performance
-            using (reader)
-            {
-                result.ResetPeople();
-
-                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 "id":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        item.SetProviderId(MetadataProviders.Tvdb, val);
-                                    }
-                                    break;
-                                }
-
-                            case "IMDB_ID":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        item.SetProviderId(MetadataProviders.Imdb, val);
-                                    }
-                                    break;
-                                }
-
-                            case "EpisodeNumber":
-                                {
-                                    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))
-                                        {
-                                            episodeNumber = rval;
-                                        }
-                                    }
-
-                                    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;
-                                }
-
-                            case "Combined_episodenumber":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
-                                        {
-                                            combinedEpisodeNumber = Convert.ToInt32(num);
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "Combined_season":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
-                                        {
-                                            combinedSeasonNumber = Convert.ToInt32(num);
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "airsbefore_episode":
-                                {
-                                    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))
-                                        {
-                                            item.AirsBeforeEpisodeNumber = rval;
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "airsafter_season":
-                                {
-                                    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))
-                                        {
-                                            item.AirsAfterSeasonNumber = rval;
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "airsbefore_season":
-                                {
-                                    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))
-                                        {
-                                            item.AirsBeforeSeasonNumber = rval;
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "EpisodeName":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!item.LockedFields.Contains(MetadataFields.Name))
-                                    {
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            item.Name = val;
-                                        }
-                                    }
-                                    break;
-                                }
-
-                            case "Overview":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!item.LockedFields.Contains(MetadataFields.Overview))
-                                    {
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            item.Overview = val;
-                                        }
-                                    }
-                                    break;
-                                }
-                            case "Rating":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        // float.TryParse is local aware, so it can be probamatic, force us culture
-                                        if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval))
-                                        {
-                                            item.CommunityRating = rval;
-                                        }
-                                    }
-                                    break;
-                                }
-                            case "RatingCount":
-                                {
-                                    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))
-                                        {
-                                            //item.VoteCount = rval;
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "FirstAired":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (DateTime.TryParse(val, out var date))
-                                        {
-                                            date = date.ToUniversalTime();
-
-                                            item.PremiereDate = date;
-                                            item.ProductionYear = date.Year;
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            case "Director":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            AddPeople(result, val, PersonType.Director);
-                                        }
-                                    }
-
-                                    break;
-                                }
-                            case "GuestStars":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            AddGuestStars(result, val);
-                                        }
-                                    }
-
-                                    break;
-                                }
-                            case "Writer":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            //AddPeople(result, val, PersonType.Writer);
-                                        }
-                                    }
-
-                                    break;
-                                }
-                            case "Language":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        result.ResultLanguage = val;
-                                    }
-
-                                    break;
-                                }
-
-                            default:
-                                reader.Skip();
-                                break;
-                        }
-                    }
-                    else
-                    {
-                        reader.Read();
-                    }
-                }
-            }
-
-            if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase))
-            {
-                episodeNumber = combinedEpisodeNumber ?? episodeNumber;
-                seasonNumber = combinedSeasonNumber ?? seasonNumber;
-            }
-
-            if (episodeNumber.HasValue)
-            {
-                item.IndexNumber = episodeNumber;
-            }
-
-            if (seasonNumber.HasValue)
-            {
-                item.ParentIndexNumber = seasonNumber;
-            }
-        }
-
-        private void AddPeople<T>(MetadataResult<T> result, string val, string personType)
-        {
-            // Sometimes tvdb actors have leading spaces
-            foreach (var person in val.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
-                                            .Where(i => !string.IsNullOrWhiteSpace(i))
-                                            .Select(str => new PersonInfo { Type = personType, Name = str.Trim() }))
-            {
-                result.AddPerson(person);
-            }
-        }
-
-        private void AddGuestStars<T>(MetadataResult<T> result, string val)
-            where T : BaseItem
-        {
-            // example:
-            // <GuestStars>|Mark C. Thomas|  Dennis Kiefer|  David Nelson (David)|  Angela Nicholas|  Tzi Ma|  Kevin P. Kearns (Pasco)|</GuestStars>
-            var persons = val.Split('|')
-                .Select(i => i.Trim())
-                .Where(i => !string.IsNullOrWhiteSpace(i))
-                .ToList();
-
-            foreach (var person in persons)
-            {
-                var index = person.IndexOf('(');
-                string role = null;
-                var name = person;
-
-                if (index != -1)
-                {
-                    role = person.Substring(index + 1).Trim().TrimEnd(')');
-
-                    name = person.Substring(0, index).Trim();
-                }
-
-                result.AddPerson(new PersonInfo
-                {
-                    Type = PersonType.GuestStar,
-                    Name = name,
-                    Role = role
-                });
-            }
-        }
-
-        private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken)
-        {
-            var item = result.Item;
-
-            // Use XmlReader for best performance
-            using (reader)
-            {
-                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 "EpisodeName":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!item.LockedFields.Contains(MetadataFields.Name))
-                                    {
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            item.Name += ", " + val;
-                                        }
-                                    }
-                                    break;
-                                }
-
-                            case "Overview":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-                                    if (!item.LockedFields.Contains(MetadataFields.Overview))
-                                    {
-                                        if (!string.IsNullOrWhiteSpace(val))
-                                        {
-                                            item.Overview += Environment.NewLine + Environment.NewLine + val;
-                                        }
-                                    }
-                                    break;
-                                }
-                            case "Director":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            AddPeople(result, val, PersonType.Director);
-                                        }
-                                    }
-
-                                    break;
-                                }
-                            case "GuestStars":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            AddGuestStars(result, val);
-                                        }
-                                    }
-
-                                    break;
-                                }
-                            case "Writer":
-                                {
-                                    var val = reader.ReadElementContentAsString();
-
-                                    if (!string.IsNullOrWhiteSpace(val))
-                                    {
-                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
-                                        {
-                                            //AddPeople(result, val, PersonType.Writer);
-                                        }
-                                    }
-
-                                    break;
-                                }
-
-                            default:
-                                reader.Skip();
-                                break;
-                        }
-                    }
-                    else
-                    {
-                        reader.Read();
-                    }
-                }
-            }
-        }
-
-        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
-        {
-            return _httpClient.GetResponse(new HttpRequestOptions
-            {
-                CancellationToken = cancellationToken,
-                Url = url
-            });
-        }
-
-        public int Order => 0;
-    }
-}
+//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.Providers;
+//using MediaBrowser.Model.Entities;
+//using MediaBrowser.Model.IO;
+//using MediaBrowser.Model.Providers;
+//using MediaBrowser.Model.Xml;
+//using Microsoft.Extensions.Logging;
+//
+//namespace MediaBrowser.Providers.TV
+//{
+//
+//    /// <summary>
+//    /// Class RemoteEpisodeProvider
+//    /// </summary>
+//    class TvdbEpisodeProvider : IRemoteMetadataProvider<Episode, EpisodeInfo>
+//    {
+//        internal static TvdbEpisodeProvider Current;
+//        private readonly IServerConfigurationManager _config;
+//        private readonly IHttpClient _httpClient;
+//        private readonly ILogger _logger;
+//
+//        public TvdbEpisodeProvider(IServerConfigurationManager config, IHttpClient httpClient, ILogger logger)
+//        {
+//            _config = config;
+//            _httpClient = httpClient;
+//            _logger = logger;
+//            Current = this;
+//        }
+//
+//        public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(EpisodeInfo searchInfo, CancellationToken cancellationToken)
+//        {
+//            var list = new List<RemoteSearchResult>();
+//
+//            // The search query must either provide an episode number or date
+//            if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue)
+//            {
+//                return Task.FromResult((IEnumerable<RemoteSearchResult>)list);
+//            }
+//
+//            if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds))
+//            {
+//                try
+//                {
+//                    var metadataResult = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
+//
+//                    if (metadataResult.HasMetadata)
+//                    {
+//                        var item = metadataResult.Item;
+//
+//                        list.Add(new RemoteSearchResult
+//                        {
+//                            IndexNumber = item.IndexNumber,
+//                            Name = item.Name,
+//                            ParentIndexNumber = item.ParentIndexNumber,
+//                            PremiereDate = item.PremiereDate,
+//                            ProductionYear = item.ProductionYear,
+//                            ProviderIds = item.ProviderIds,
+//                            SearchProviderName = Name,
+//                            IndexNumberEnd = item.IndexNumberEnd
+//                        });
+//                    }
+//                }
+//                catch (FileNotFoundException)
+//                {
+//                    // Don't fail the provider because this will just keep on going and going.
+//                }
+//                catch (IOException)
+//                {
+//                    // Don't fail the provider because this will just keep on going and going.
+//                }
+//            }
+//
+//            return Task.FromResult((IEnumerable<RemoteSearchResult>)list);
+//        }
+//
+//        public string Name => "TheTVDB";
+//
+//        public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo searchInfo, CancellationToken cancellationToken)
+//        {
+//            var result = new MetadataResult<Episode>();
+//            result.QueriedById = true;
+//
+//            if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) &&
+//                (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue))
+//            {
+//                result = FetchEpisodeData(searchInfo, seriesDataPath, cancellationToken);
+//            }
+//            else
+//            {
+//                _logger.LogDebug("No series identity found for {0}", searchInfo.Name);
+//            }
+//
+//            return result;
+//        }
+//
+//        /// <summary>
+//        /// Gets the episode XML files.
+//        /// </summary>
+//        /// <param name="seriesDataPath">The series data path.</param>
+//        /// <param name="searchInfo">The search information.</param>
+//        /// <returns>List{FileInfo}.</returns>
+//        internal List<XmlReader> GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo)
+//        {
+//            var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage);
+//
+//            try
+//            {
+//                return GetXmlNodes(seriesXmlPath, searchInfo);
+//            }
+//            catch (FileNotFoundException)
+//            {
+//                return new List<XmlReader>();
+//            }
+//            catch (IOException)
+//            {
+//                return new List<XmlReader>();
+//            }
+//        }
+//
+//        /// <summary>
+//        /// Fetches the episode data.
+//        /// </summary>
+//        /// <param name="id">The identifier.</param>
+//        /// <param name="seriesDataPath">The series data path.</param>
+//        /// <param name="cancellationToken">The cancellation token.</param>
+//        /// <returns>Task{System.Boolean}.</returns>
+//        private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, string seriesDataPath, CancellationToken cancellationToken)
+//        {
+//            var result = new MetadataResult<Episode>()
+//            {
+//                Item = new Episode
+//                {
+//                    IndexNumber = id.IndexNumber,
+//                    ParentIndexNumber = id.ParentIndexNumber,
+//                    IndexNumberEnd = id.IndexNumberEnd
+//                }
+//            };
+//
+//            var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id);
+//
+//            if (xmlNodes.Count > 0)
+//            {
+//                FetchMainEpisodeInfo(result, xmlNodes[0], id.SeriesDisplayOrder, cancellationToken);
+//
+//                result.HasMetadata = true;
+//            }
+//
+//            foreach (var node in xmlNodes.Skip(1))
+//            {
+//                FetchAdditionalPartInfo(result, node, cancellationToken);
+//            }
+//
+//            return result;
+//        }
+//
+//        private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo)
+//        {
+//            var list = new List<XmlReader>();
+//
+//            if (searchInfo.IndexNumber.HasValue)
+//            {
+//                var files = GetEpisodeXmlFiles(searchInfo.SeriesDisplayOrder, searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile));
+//
+//                list = files.Select(GetXmlReader).ToList();
+//            }
+//
+//            if (list.Count == 0 && searchInfo.PremiereDate.HasValue)
+//            {
+//                list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value);
+//            }
+//
+//            return list;
+//        }
+//
+//        private string GetEpisodeFileName(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
+//        {
+//            if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase))
+//            {
+//                return string.Format("episode-abs-{0}.xml", episodeNumber);
+//            }
+//            else if (string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+//            {
+//                return string.Format("episode-dvd-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
+//            }
+//            else
+//            {
+//                return string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber);
+//            }
+//        }
+//
+//        private FileSystemMetadata GetEpisodeFileInfoWithFallback(string seriesDataPath, string seriesDisplayOrder, int? seasonNumber, int? episodeNumber)
+//        {
+//            var file = Path.Combine(seriesDataPath, GetEpisodeFileName(seriesDisplayOrder, seasonNumber, episodeNumber));
+//            var fileInfo = _fileSystem.GetFileInfo(file);
+//
+//            if (fileInfo.Exists)
+//            {
+//                return fileInfo;
+//            }
+//
+//            if (!seasonNumber.HasValue)
+//            {
+//                return fileInfo;
+//            }
+//
+//            // revert to aired order
+//            if (string.Equals(seriesDisplayOrder, "absolute", StringComparison.OrdinalIgnoreCase) || string.Equals(seriesDisplayOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+//            {
+//                file = Path.Combine(seriesDataPath, GetEpisodeFileName(null, seasonNumber, episodeNumber));
+//                return _fileSystem.GetFileInfo(file);
+//            }
+//
+//            return fileInfo;
+//        }
+//
+//        private List<FileSystemMetadata> GetEpisodeXmlFiles(string seriesDisplayOrder, int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath)
+//        {
+//            var files = new List<FileSystemMetadata>();
+//
+//            if (episodeNumber == null)
+//            {
+//                return files;
+//            }
+//
+//            if (!seasonNumber.HasValue)
+//            {
+//                seriesDisplayOrder = "absolute";
+//            }
+//
+//            var fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
+//
+//            if (fileInfo.Exists)
+//            {
+//                files.Add(fileInfo);
+//            }
+//
+//            var end = endingEpisodeNumber ?? episodeNumber;
+//            episodeNumber++;
+//
+//            while (episodeNumber <= end)
+//            {
+//                fileInfo = GetEpisodeFileInfoWithFallback(seriesDataPath, seriesDisplayOrder, seasonNumber, episodeNumber);
+//
+//                if (fileInfo.Exists)
+//                {
+//                    files.Add(fileInfo);
+//                }
+//                else
+//                {
+//                    break;
+//                }
+//
+//                episodeNumber++;
+//            }
+//
+//            return files;
+//        }
+//
+//        private XmlReader GetXmlReader(FileSystemMetadata xmlFile)
+//        {
+//            return GetXmlReader(File.ReadAllText(xmlFile.FullName, Encoding.UTF8));
+//        }
+//
+//        private XmlReader GetXmlReader(string xml)
+//        {
+//            var streamReader = new StringReader(xml);
+//
+//            var settings = _xmlSettings.Create(false);
+//
+//            settings.CheckCharacters = false;
+//            settings.IgnoreProcessingInstructions = true;
+//            settings.IgnoreComments = true;
+//
+//            return XmlReader.Create(streamReader, settings);
+//        }
+//
+//        private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate)
+//        {
+//            var list = new List<XmlReader>();
+//
+//            using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read))
+//            {
+//                using (var streamReader = new StreamReader(fileStream, Encoding.UTF8))
+//                {
+//                    // Use XmlReader for best performance
+//
+//                    var settings = _xmlSettings.Create(false);
+//
+//                    settings.CheckCharacters = false;
+//                    settings.IgnoreProcessingInstructions = true;
+//                    settings.IgnoreComments = true;
+//
+//                    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();
+//
+//                                            var airDate = GetEpisodeAirDate(outerXml);
+//
+//                                            if (airDate.HasValue && premiereDate.Date == airDate.Value.Date)
+//                                            {
+//                                                list.Add(GetXmlReader(outerXml));
+//                                                return list;
+//                                            }
+//
+//                                            break;
+//                                        }
+//
+//                                    default:
+//                                        reader.Skip();
+//                                        break;
+//                                }
+//                            }
+//                            else
+//                            {
+//                                reader.Read();
+//                            }
+//                        }
+//                    }
+//                }
+//            }
+//
+//            return list;
+//        }
+//
+//        private DateTime? GetEpisodeAirDate(string xml)
+//        {
+//            using (var streamReader = new StringReader(xml))
+//            {
+//                var settings = _xmlSettings.Create(false);
+//
+//                settings.CheckCharacters = false;
+//                settings.IgnoreProcessingInstructions = true;
+//                settings.IgnoreComments = true;
+//
+//                // 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 "FirstAired":
+//                                    {
+//                                        var val = reader.ReadElementContentAsString();
+//
+//                                        if (!string.IsNullOrWhiteSpace(val))
+//                                        {
+//                                            if (DateTime.TryParse(val, out var date))
+//                                            {
+//                                                date = date.ToUniversalTime();
+//
+//                                                return date;
+//                                            }
+//                                        }
+//
+//                                        break;
+//                                    }
+//
+//                                default:
+//                                    reader.Skip();
+//                                    break;
+//                            }
+//                        }
+//                        else
+//                        {
+//                            reader.Read();
+//                        }
+//                    }
+//                }
+//            }
+//            return null;
+//        }
+//
+//        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+//
+//        private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, string seriesOrder, CancellationToken cancellationToken)
+//        {
+//            var item = result.Item;
+//
+//            int? episodeNumber = null;
+//            int? seasonNumber = null;
+//            int? combinedEpisodeNumber = null;
+//            int? combinedSeasonNumber = null;
+//
+//            // Use XmlReader for best performance
+//            using (reader)
+//            {
+//                result.ResetPeople();
+//
+//                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 "id":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        item.SetProviderId(MetadataProviders.Tvdb, val);
+//                                    }
+//                                    break;
+//                                }
+//
+//                            case "IMDB_ID":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        item.SetProviderId(MetadataProviders.Imdb, val);
+//                                    }
+//                                    break;
+//                                }
+//
+//                            case "EpisodeNumber":
+//                                {
+//                                    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))
+//                                        {
+//                                            episodeNumber = rval;
+//                                        }
+//                                    }
+//
+//                                    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;
+//                                }
+//
+//                            case "Combined_episodenumber":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
+//                                        {
+//                                            combinedEpisodeNumber = Convert.ToInt32(num);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "Combined_season":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (float.TryParse(val, NumberStyles.Any, _usCulture, out var num))
+//                                        {
+//                                            combinedSeasonNumber = Convert.ToInt32(num);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "airsbefore_episode":
+//                                {
+//                                    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))
+//                                        {
+//                                            item.AirsBeforeEpisodeNumber = rval;
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "airsafter_season":
+//                                {
+//                                    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))
+//                                        {
+//                                            item.AirsAfterSeasonNumber = rval;
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "airsbefore_season":
+//                                {
+//                                    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))
+//                                        {
+//                                            item.AirsBeforeSeasonNumber = rval;
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "EpisodeName":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!item.LockedFields.Contains(MetadataFields.Name))
+//                                    {
+//                                        if (!string.IsNullOrWhiteSpace(val))
+//                                        {
+//                                            item.Name = val;
+//                                        }
+//                                    }
+//                                    break;
+//                                }
+//
+//                            case "Overview":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!item.LockedFields.Contains(MetadataFields.Overview))
+//                                    {
+//                                        if (!string.IsNullOrWhiteSpace(val))
+//                                        {
+//                                            item.Overview = val;
+//                                        }
+//                                    }
+//                                    break;
+//                                }
+//                            case "Rating":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        // float.TryParse is local aware, so it can be probamatic, force us culture
+//                                        if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out var rval))
+//                                        {
+//                                            item.CommunityRating = rval;
+//                                        }
+//                                    }
+//                                    break;
+//                                }
+//                            case "RatingCount":
+//                                {
+//                                    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))
+//                                        {
+//                                            //item.VoteCount = rval;
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "FirstAired":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (DateTime.TryParse(val, out var date))
+//                                        {
+//                                            date = date.ToUniversalTime();
+//
+//                                            item.PremiereDate = date;
+//                                            item.ProductionYear = date.Year;
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            case "Director":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            AddPeople(result, val, PersonType.Director);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//                            case "GuestStars":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            AddGuestStars(result, val);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//                            case "Writer":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            //AddPeople(result, val, PersonType.Writer);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//                            case "Language":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        result.ResultLanguage = val;
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            default:
+//                                reader.Skip();
+//                                break;
+//                        }
+//                    }
+//                    else
+//                    {
+//                        reader.Read();
+//                    }
+//                }
+//            }
+//
+//            if (string.Equals(seriesOrder, "dvd", StringComparison.OrdinalIgnoreCase))
+//            {
+//                episodeNumber = combinedEpisodeNumber ?? episodeNumber;
+//                seasonNumber = combinedSeasonNumber ?? seasonNumber;
+//            }
+//
+//            if (episodeNumber.HasValue)
+//            {
+//                item.IndexNumber = episodeNumber;
+//            }
+//
+//            if (seasonNumber.HasValue)
+//            {
+//                item.ParentIndexNumber = seasonNumber;
+//            }
+//        }
+//
+//        private void AddPeople<T>(MetadataResult<T> result, string val, string personType)
+//        {
+//            // Sometimes tvdb actors have leading spaces
+//            foreach (var person in val.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries)
+//                                            .Where(i => !string.IsNullOrWhiteSpace(i))
+//                                            .Select(str => new PersonInfo { Type = personType, Name = str.Trim() }))
+//            {
+//                result.AddPerson(person);
+//            }
+//        }
+//
+//        private void AddGuestStars<T>(MetadataResult<T> result, string val)
+//            where T : BaseItem
+//        {
+//            // example:
+//            // <GuestStars>|Mark C. Thomas|  Dennis Kiefer|  David Nelson (David)|  Angela Nicholas|  Tzi Ma|  Kevin P. Kearns (Pasco)|</GuestStars>
+//            var persons = val.Split('|')
+//                .Select(i => i.Trim())
+//                .Where(i => !string.IsNullOrWhiteSpace(i))
+//                .ToList();
+//
+//            foreach (var person in persons)
+//            {
+//                var index = person.IndexOf('(');
+//                string role = null;
+//                var name = person;
+//
+//                if (index != -1)
+//                {
+//                    role = person.Substring(index + 1).Trim().TrimEnd(')');
+//
+//                    name = person.Substring(0, index).Trim();
+//                }
+//
+//                result.AddPerson(new PersonInfo
+//                {
+//                    Type = PersonType.GuestStar,
+//                    Name = name,
+//                    Role = role
+//                });
+//            }
+//        }
+//
+//        private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken)
+//        {
+//            var item = result.Item;
+//
+//            // Use XmlReader for best performance
+//            using (reader)
+//            {
+//                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 "EpisodeName":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!item.LockedFields.Contains(MetadataFields.Name))
+//                                    {
+//                                        if (!string.IsNullOrWhiteSpace(val))
+//                                        {
+//                                            item.Name += ", " + val;
+//                                        }
+//                                    }
+//                                    break;
+//                                }
+//
+//                            case "Overview":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//                                    if (!item.LockedFields.Contains(MetadataFields.Overview))
+//                                    {
+//                                        if (!string.IsNullOrWhiteSpace(val))
+//                                        {
+//                                            item.Overview += Environment.NewLine + Environment.NewLine + val;
+//                                        }
+//                                    }
+//                                    break;
+//                                }
+//                            case "Director":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            AddPeople(result, val, PersonType.Director);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//                            case "GuestStars":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            AddGuestStars(result, val);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//                            case "Writer":
+//                                {
+//                                    var val = reader.ReadElementContentAsString();
+//
+//                                    if (!string.IsNullOrWhiteSpace(val))
+//                                    {
+//                                        if (!item.LockedFields.Contains(MetadataFields.Cast))
+//                                        {
+//                                            //AddPeople(result, val, PersonType.Writer);
+//                                        }
+//                                    }
+//
+//                                    break;
+//                                }
+//
+//                            default:
+//                                reader.Skip();
+//                                break;
+//                        }
+//                    }
+//                    else
+//                    {
+//                        reader.Read();
+//                    }
+//                }
+//            }
+//        }
+//
+//        public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
+//        {
+//            return _httpClient.GetResponse(new HttpRequestOptions
+//            {
+//                CancellationToken = cancellationToken,
+//                Url = url
+//            });
+//        }
+//
+//        public int Order => 0;
+//    }
+//}

+ 5 - 2
MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs

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

+ 56 - 244
MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs

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

+ 63 - 252
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs

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

+ 51 - 828
MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs

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