From 872aec9352d1e9aebf2c6f6722ff23a8aebb39a8 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Thu, 27 Oct 2016 03:58:33 -0400 Subject: [PATCH] continue reworking provider project --- .../Library/ItemResolveArgs.cs | 12 - .../Globalization/ILocalizationManager.cs | 2 + .../Manager/ProviderManager.cs | 40 +- .../MediaBrowser.Providers.csproj | 1 - .../MediaInfo/FFProbeVideoInfo.cs | 19 +- .../Music/MusicBrainzArtistProvider.cs | 17 +- .../TV/MissingEpisodeProvider.cs | 7 +- .../TV/TheTVDB/TvdbEpisodeProvider.cs | 1133 +++++++++-------- .../TV/TheTVDB/TvdbSeasonImageProvider.cs | 62 +- .../TV/TheTVDB/TvdbSeriesImageProvider.cs | 60 +- .../TV/TheTVDB/TvdbSeriesProvider.cs | 317 +++-- .../Localization/LocalizationManager.cs | 5 + ...MediaBrowser.Server.Implementations.csproj | 5 + .../TV/SeriesPostScanTask.cs | 21 +- 14 files changed, 875 insertions(+), 826 deletions(-) rename {MediaBrowser.Providers => MediaBrowser.Server.Implementations}/TV/SeriesPostScanTask.cs (97%) diff --git a/MediaBrowser.Controller/Library/ItemResolveArgs.cs b/MediaBrowser.Controller/Library/ItemResolveArgs.cs index 3cb927716a..763d27ebaf 100644 --- a/MediaBrowser.Controller/Library/ItemResolveArgs.cs +++ b/MediaBrowser.Controller/Library/ItemResolveArgs.cs @@ -98,18 +98,6 @@ namespace MediaBrowser.Controller.Library } } - /// - /// Gets a value indicating whether this instance is hidden. - /// - /// true if this instance is hidden; otherwise, false. - public bool IsHidden - { - get - { - return FileInfo.IsHidden; - } - } - /// /// Gets a value indicating whether this instance is vf. /// diff --git a/MediaBrowser.Model/Globalization/ILocalizationManager.cs b/MediaBrowser.Model/Globalization/ILocalizationManager.cs index b0e1d7fa68..4477d8de35 100644 --- a/MediaBrowser.Model/Globalization/ILocalizationManager.cs +++ b/MediaBrowser.Model/Globalization/ILocalizationManager.cs @@ -52,5 +52,7 @@ namespace MediaBrowser.Model.Globalization IEnumerable GetLocalizationOptions(); string RemoveDiacritics(string text); + + string NormalizeFormKD(string text); } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index 92ce0a036a..ba4b624e3c 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -863,8 +863,8 @@ namespace MediaBrowser.Providers.Manager private readonly ConcurrentQueue> _refreshQueue = new ConcurrentQueue>(); - private readonly object _refreshTimerLock = new object(); - private Timer _refreshTimer; + private readonly object _refreshQueueLock = new object(); + private bool _isProcessingRefreshQueue; public void QueueRefresh(Guid id, MetadataRefreshOptions options) { @@ -874,38 +874,18 @@ namespace MediaBrowser.Providers.Manager } _refreshQueue.Enqueue(new Tuple(id, options)); - StartRefreshTimer(); - } - private void StartRefreshTimer() - { - if (_disposed) + lock (_refreshQueueLock) { - return; - } - - lock (_refreshTimerLock) - { - if (_refreshTimer == null) + if (!_isProcessingRefreshQueue) { - _refreshTimer = new Timer(RefreshTimerCallback, null, 100, Timeout.Infinite); + _isProcessingRefreshQueue = true; + Task.Run(() => StartProcessingRefreshQueue()); } } } - private void StopRefreshTimer() - { - lock (_refreshTimerLock) - { - if (_refreshTimer != null) - { - _refreshTimer.Dispose(); - _refreshTimer = null; - } - } - } - - private async void RefreshTimerCallback(object state) + private async Task StartProcessingRefreshQueue() { Tuple refreshItem; var libraryManager = _libraryManagerFactory(); @@ -939,7 +919,10 @@ namespace MediaBrowser.Providers.Manager } } - StopRefreshTimer(); + lock (_refreshQueueLock) + { + _isProcessingRefreshQueue = false; + } } private async Task RefreshItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken) @@ -1018,7 +1001,6 @@ namespace MediaBrowser.Providers.Manager public void Dispose() { _disposed = true; - StopRefreshTimer(); } } } \ No newline at end of file diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 016fa2ebd0..e2ee40139c 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -131,7 +131,6 @@ - diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index 8dc4c42902..45fcbc251e 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -134,23 +134,6 @@ namespace MediaBrowser.Providers.MediaInfo { cancellationToken.ThrowIfCancellationRequested(); - //var idString = item.Id.ToString("N"); - //var cachePath = Path.Combine(_appPaths.CachePath, - // "ffprobe-video", - // idString.Substring(0, 2), idString, "v" + SchemaVersion + _mediaEncoder.Version + item.DateModified.Ticks.ToString(_usCulture) + ".json"); - - try - { - //return _json.DeserializeFromFile(cachePath); - } - catch (FileNotFoundException) - { - - } - catch (DirectoryNotFoundException) - { - } - var protocol = item.LocationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File; @@ -655,7 +638,7 @@ namespace MediaBrowser.Providers.MediaInfo /// System.Nullable{IsoType}. private IsoType? DetermineIsoType(IIsoMount isoMount) { - var fileSystemEntries = Directory.EnumerateFileSystemEntries(isoMount.MountedPath).Select(Path.GetFileName).ToList(); + var fileSystemEntries = _fileSystem.GetFileSystemEntryPaths(isoMount.MountedPath).Select(Path.GetFileName).ToList(); if (fileSystemEntries.Contains("video_ts", StringComparer.OrdinalIgnoreCase) || fileSystemEntries.Contains("VIDEO_TS.IFO", StringComparer.OrdinalIgnoreCase)) diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index 88128bdc7a..adb771e7c6 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -12,6 +12,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Controller.Extensions; namespace MediaBrowser.Providers.Music { @@ -159,21 +160,7 @@ namespace MediaBrowser.Providers.Music /// true if the specified text has diacritics; otherwise, false. private bool HasDiacritics(string text) { - return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); - } - - /// - /// Removes the diacritics. - /// - /// The text. - /// System.String. - private string RemoveDiacritics(string text) - { - return String.Concat( - text.Normalize(NormalizationForm.FormD) - .Where(ch => CharUnicodeInfo.GetUnicodeCategory(ch) != - UnicodeCategory.NonSpacingMark) - ).Normalize(NormalizationForm.FormC); + return !String.Equals(text, text.RemoveDiacritics(), StringComparison.Ordinal); } /// diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index d0321ea339..b82320fae2 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -20,7 +20,7 @@ using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { - class MissingEpisodeProvider + public class MissingEpisodeProvider { private readonly IServerConfigurationManager _config; private readonly ILogger _logger; @@ -56,7 +56,7 @@ namespace MediaBrowser.Providers.TV { break; } - catch (DirectoryNotFoundException) + catch (IOException) { //_logger.Warn("Series files missing for series id {0}", seriesGroup.Key); } @@ -80,7 +80,8 @@ namespace MediaBrowser.Providers.TV var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); - var episodeFiles = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly) + var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath) + .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) .Select(Path.GetFileNameWithoutExtension) .Where(i => i.StartsWith("episode-", StringComparison.OrdinalIgnoreCase)) .ToList(); diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 7f13edf602..139daf9a39 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -19,6 +19,7 @@ using System.Xml; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -35,13 +36,15 @@ namespace MediaBrowser.Providers.TV 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) + public TvdbEpisodeProvider(IFileSystem fileSystem, IServerConfigurationManager config, IHttpClient httpClient, ILogger logger, IXmlReaderSettingsFactory xmlSettings) { _fileSystem = fileSystem; _config = config; _httpClient = httpClient; _logger = logger; + _xmlSettings = xmlSettings; Current = this; } @@ -49,11 +52,11 @@ namespace MediaBrowser.Providers.TV { var list = new List(); - // The search query must either provide an episode number or date - if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) - { - return list; - } + // The search query must either provide an episode number or date + if (!searchInfo.IndexNumber.HasValue && !searchInfo.PremiereDate.HasValue) + { + return list; + } if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds)) { @@ -61,12 +64,13 @@ namespace MediaBrowser.Providers.TV var searchNumbers = new EpisodeNumbers(); - if (searchInfo.IndexNumber.HasValue) { - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; - } - + if (searchInfo.IndexNumber.HasValue) + { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } + searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; - searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; + searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; try { @@ -93,7 +97,7 @@ namespace MediaBrowser.Providers.TV { // Don't fail the provider because this will just keep on going and going. } - catch (DirectoryNotFoundException) + catch (IOException) { // Don't fail the provider because this will just keep on going and going. } @@ -112,19 +116,20 @@ namespace MediaBrowser.Providers.TV var result = new MetadataResult(); result.QueriedById = true; - if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && - (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) + if (TvdbSeriesProvider.IsValidSeries(searchInfo.SeriesProviderIds) && + (searchInfo.IndexNumber.HasValue || searchInfo.PremiereDate.HasValue)) { await TvdbSeriesProvider.Current.EnsureSeriesInfo(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, searchInfo.SeriesProviderIds); var searchNumbers = new EpisodeNumbers(); - if (searchInfo.IndexNumber.HasValue) { - searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; - } + if (searchInfo.IndexNumber.HasValue) + { + searchNumbers.EpisodeNumber = searchInfo.IndexNumber.Value; + } searchNumbers.SeasonNumber = searchInfo.ParentIndexNumber; - searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; + searchNumbers.EpisodeNumberEnd = searchInfo.IndexNumberEnd ?? searchNumbers.EpisodeNumber; try { @@ -134,7 +139,7 @@ namespace MediaBrowser.Providers.TV { // Don't fail the provider because this will just keep on going and going. } - catch (DirectoryNotFoundException) + catch (IOException) { // Don't fail the provider because this will just keep on going and going. } @@ -155,20 +160,20 @@ namespace MediaBrowser.Providers.TV /// List{FileInfo}. internal List GetEpisodeXmlNodes(string seriesDataPath, EpisodeInfo searchInfo) { - var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath (searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); - - try - { - return GetXmlNodes(seriesXmlPath, searchInfo); - } - catch (DirectoryNotFoundException) - { - return new List (); - } - catch (FileNotFoundException) - { - return new List (); - } + var seriesXmlPath = TvdbSeriesProvider.Current.GetSeriesXmlPath(searchInfo.SeriesProviderIds, searchInfo.MetadataLanguage); + + try + { + return GetXmlNodes(seriesXmlPath, searchInfo); + } + catch (FileNotFoundException) + { + return new List(); + } + catch (IOException) + { + return new List(); + } } private class EpisodeNumbers @@ -188,59 +193,62 @@ namespace MediaBrowser.Providers.TV /// Task{System.Boolean}. private MetadataResult FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken) { - var result = new MetadataResult() - { - Item = new Episode - { - IndexNumber = id.IndexNumber, - ParentIndexNumber = id.ParentIndexNumber, - IndexNumberEnd = id.IndexNumberEnd - } - }; + var result = new MetadataResult() + { + Item = new Episode + { + IndexNumber = id.IndexNumber, + ParentIndexNumber = id.ParentIndexNumber, + IndexNumberEnd = id.IndexNumberEnd + } + }; - var xmlNodes = GetEpisodeXmlNodes (seriesDataPath, id); + var xmlNodes = GetEpisodeXmlNodes(seriesDataPath, id); - if (xmlNodes.Count > 0) { - FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken); + if (xmlNodes.Count > 0) + { + FetchMainEpisodeInfo(result, xmlNodes[0], cancellationToken); - result.HasMetadata = true; - } + result.HasMetadata = true; + } - foreach (var node in xmlNodes.Skip(1)) { - FetchAdditionalPartInfo(result, node, cancellationToken); - } + foreach (var node in xmlNodes.Skip(1)) + { + FetchAdditionalPartInfo(result, node, cancellationToken); + } return result; } - private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) - { - var list = new List (); + private List GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) + { + var list = new List(); - if (searchInfo.IndexNumber.HasValue) - { - var files = GetEpisodeXmlFiles (searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName (xmlFile)); + if (searchInfo.IndexNumber.HasValue) + { + var files = GetEpisodeXmlFiles(searchInfo.ParentIndexNumber, searchInfo.IndexNumber, searchInfo.IndexNumberEnd, Path.GetDirectoryName(xmlFile)); - list = files.Select (GetXmlReader).ToList (); - } + list = files.Select(GetXmlReader).ToList(); + } - if (list.Count == 0 && searchInfo.PremiereDate.HasValue) { - list = GetXmlNodesByPremiereDate (xmlFile, searchInfo.PremiereDate.Value); - } + if (list.Count == 0 && searchInfo.PremiereDate.HasValue) + { + list = GetXmlNodesByPremiereDate(xmlFile, searchInfo.PremiereDate.Value); + } - return list; - } + return list; + } - private List GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) - { - var files = new List(); + private List GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + { + var files = new List(); - if (episodeNumber == null) - { - return files; - } + if (episodeNumber == null) + { + return files; + } - var usingAbsoluteData = false; + var usingAbsoluteData = false; if (seasonNumber.HasValue) { @@ -263,230 +271,283 @@ namespace MediaBrowser.Providers.TV } } - var end = endingEpisodeNumber ?? episodeNumber; - episodeNumber++; + var end = endingEpisodeNumber ?? episodeNumber; + episodeNumber++; - while (episodeNumber <= end) - { + while (episodeNumber <= end) + { string file; - if (usingAbsoluteData) - { - file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); - } - else - { - file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); - } + if (usingAbsoluteData) + { + file = Path.Combine(seriesDataPath, string.Format("episode-abs-{0}.xml", episodeNumber)); + } + else + { + file = Path.Combine(seriesDataPath, string.Format("episode-{0}-{1}.xml", seasonNumber.Value, episodeNumber)); + } - var fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - break; - } + var fileInfo = _fileSystem.GetFileInfo(file); + if (fileInfo.Exists) + { + files.Add(fileInfo); + } + else + { + break; + } - episodeNumber++; - } + episodeNumber++; + } - return files; - } + return files; + } - private XmlReader GetXmlReader(FileSystemMetadata xmlFile) - { - return GetXmlReader (_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8)); - } + private XmlReader GetXmlReader(FileSystemMetadata xmlFile) + { + return GetXmlReader(_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8)); + } - private XmlReader GetXmlReader(String xml) - { - var streamReader = new StringReader (xml); + private XmlReader GetXmlReader(String xml) + { + var streamReader = new StringReader(xml); - return XmlReader.Create (streamReader, new XmlReaderSettings { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }); - } + var settings = _xmlSettings.Create(false); - private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) - { - var list = new List (); + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - using (var streamReader = new StreamReader (xmlFile, Encoding.UTF8)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); + return XmlReader.Create(streamReader, settings); + } - // Loop through each element - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "Episode": - { - var outerXml = reader.ReadOuterXml(); + private List GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) + { + var list = new List(); - var airDate = GetEpisodeAirDate (outerXml); + 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 - if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) - { - list.Add (GetXmlReader(outerXml)); - return list; - } + var settings = _xmlSettings.Create(false); - break; - } + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - default: - reader.Skip(); - break; - } - } - } - } - } + using (var reader = XmlReader.Create(streamReader, settings)) + { + reader.MoveToContent(); - return list; - } + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Episode": + { + var outerXml = reader.ReadOuterXml(); - private DateTime? GetEpisodeAirDate(string xml) - { - using (var streamReader = new StringReader (xml)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create (streamReader, new XmlReaderSettings { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent (); + var airDate = GetEpisodeAirDate(outerXml); - // Loop through each element - while (reader.Read ()) { + if (airDate.HasValue && premiereDate.Date == airDate.Value.Date) + { + list.Add(GetXmlReader(outerXml)); + return list; + } - if (reader.NodeType == XmlNodeType.Element) { - switch (reader.Name) { + break; + } - case "FirstAired": - { - var val = reader.ReadElementContentAsString (); + default: + reader.Skip(); + break; + } + } + } + } + } + } - if (!string.IsNullOrWhiteSpace (val)) { - DateTime date; - if (DateTime.TryParse (val, out date)) { - date = date.ToUniversalTime (); + return list; + } - return date; - } - } + private DateTime? GetEpisodeAirDate(string xml) + { + using (var streamReader = new StringReader(xml)) + { + var settings = _xmlSettings.Create(false); - break; - } + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - default: - reader.Skip (); - break; - } - } - } - } - } - return null; - } + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out date)) + { + date = date.ToUniversalTime(); + + return date; + } + } + + break; + } + + default: + reader.Skip(); + break; + } + } + } + } + } + return null; + } private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) + private void FetchMainEpisodeInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); - result.ResetPeople(); + result.ResetPeople(); - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); + // Loop through each element + while (reader.Read()) + { + 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 "DVD_episodenumber": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) - { - item.DvdEpisodeNumber = num; - } - } - - break; - } - - case "DVD_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "id": { - float num; - - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) { - item.DvdSeasonNumber = Convert.ToInt32(num); + item.SetProviderId(MetadataProviders.Tvdb, val); } + break; } - break; - } + case "IMDB_ID": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Imdb, val); + } + break; + } - case "EpisodeNumber": - { - if (!item.IndexNumber.HasValue) + case "DVD_episodenumber": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float num; + + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + { + item.DvdEpisodeNumber = num; + } + } + + break; + } + + case "DVD_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float num; + + if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + { + item.DvdSeasonNumber = Convert.ToInt32(num); + } + } + + break; + } + + case "EpisodeNumber": + { + if (!item.IndexNumber.HasValue) + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.IndexNumber = rval; + } + } + } + + break; + } + + case "SeasonNumber": + { + if (!item.ParentIndexNumber.HasValue) + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.ParentIndexNumber = rval; + } + } + } + + break; + } + + case "absolute_number": { var val = reader.ReadElementContentAsString(); @@ -497,17 +558,14 @@ namespace MediaBrowser.Providers.TV // int.TryParse is local aware, so it can be probamatic, force us culture if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - item.IndexNumber = rval; + item.AbsoluteEpisodeNumber = rval; } } + + break; } - break; - } - - case "SeasonNumber": - { - if (!item.ParentIndexNumber.HasValue) + case "airsbefore_episode": { var val = reader.ReadElementContentAsString(); @@ -518,225 +576,188 @@ namespace MediaBrowser.Providers.TV // int.TryParse is local aware, so it can be probamatic, force us culture if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) { - item.ParentIndexNumber = rval; + item.AirsBeforeEpisodeNumber = rval; } } + + break; } + case "airsafter_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsAfterSeasonNumber = rval; + } + } + + break; + } + + case "airsbefore_season": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.AirsBeforeSeasonNumber = rval; + } + } + + break; + } + + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name = val; + } + } + break; + } + + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Overview = val; + } + } + break; + } + case "Rating": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + float rval; + + // float.TryParse is local aware, so it can be probamatic, force us culture + if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) + { + item.CommunityRating = rval; + } + } + break; + } + case "RatingCount": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + int rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + item.VoteCount = rval; + } + } + + break; + } + + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out 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; - } - - case "absolute_number": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AbsoluteEpisodeNumber = rval; - } - } - - break; - } - - case "airsbefore_episode": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeEpisodeNumber = rval; - } - } - - break; - } - - case "airsafter_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsAfterSeasonNumber = rval; - } - } - - break; - } - - case "airsbefore_season": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.AirsBeforeSeasonNumber = rval; - } - } - - break; - } - - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name = val; - } - } - break; - } - - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview = val; - } - } - break; - } - case "Rating": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - float rval; - - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(val, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) - { - item.CommunityRating = rval; - } - } - break; - } - case "RatingCount": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - int rval; - - // int.TryParse is local aware, so it can be probamatic, force us culture - if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) - { - item.VoteCount = rval; - } - } - - break; - } - - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - DateTime date; - if (DateTime.TryParse(val, out 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; - } - } - } - } + } + } + } + } } private void AddPeople(MetadataResult result, string val, string personType) @@ -780,99 +801,99 @@ namespace MediaBrowser.Providers.TV } } - private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) + private void FetchAdditionalPartInfo(MetadataResult result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); + // Use XmlReader for best performance + using (reader) + { + reader.MoveToContent(); - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "EpisodeName": - { - if (!item.LockedFields.Contains(MetadataFields.Name)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Name += ", " + val; - } - } - break; - } + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "EpisodeName": + { + if (!item.LockedFields.Contains(MetadataFields.Name)) + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.Name += ", " + val; + } + } + break; + } - case "Overview": - { - if (!item.LockedFields.Contains(MetadataFields.Overview)) - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - item.Overview += Environment.NewLine + Environment.NewLine + val; - } - } - break; - } - case "Director": - { - var val = reader.ReadElementContentAsString(); + case "Overview": + { + if (!item.LockedFields.Contains(MetadataFields.Overview)) + { + var val = reader.ReadElementContentAsString(); + 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); - } - } + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Director); + } + } - break; - } - case "GuestStars": - { - var val = reader.ReadElementContentAsString(); + break; + } + case "GuestStars": + { + var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddGuestStars(result, val); - } - } + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddGuestStars(result, val); + } + } - break; - } - case "Writer": - { - var val = reader.ReadElementContentAsString(); + break; + } + case "Writer": + { + var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - if (!item.LockedFields.Contains(MetadataFields.Cast)) - { - AddPeople(result, val, PersonType.Writer); - } - } + if (!string.IsNullOrWhiteSpace(val)) + { + if (!item.LockedFields.Contains(MetadataFields.Cast)) + { + AddPeople(result, val, PersonType.Writer); + } + } - break; - } + break; + } - default: - reader.Skip(); - break; - } - } - } - } + default: + reader.Skip(); + break; + } + } + } + } } public Task GetImageResponse(string url, CancellationToken cancellationToken) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 766fc951b0..1a611ee9b9 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -19,6 +19,7 @@ using System.Xml; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -29,12 +30,14 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _config; private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; + private readonly IXmlReaderSettingsFactory _xmlSettings; - public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) + public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) { _config = config; _httpClient = httpClient; _fileSystem = fileSystem; + _xmlSettings = xmlSettings; } public string Name @@ -80,13 +83,13 @@ namespace MediaBrowser.Providers.TV try { - return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, cancellationToken); + return GetImages(path, item.GetPreferredMetadataLanguage(), seasonNumber, _xmlSettings, _fileSystem, cancellationToken); } catch (FileNotFoundException) { // No tvdb data yet. Don't blow up } - catch (DirectoryNotFoundException) + catch (IOException) { // No tvdb data yet. Don't blow up } @@ -105,45 +108,46 @@ namespace MediaBrowser.Providers.TV return seasonNumber; } - internal static IEnumerable GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken) + internal static IEnumerable GetImages(string xmlPath, string preferredLanguage, int seasonNumber, IXmlReaderSettingsFactory xmlReaderSettingsFactory, IFileSystem fileSystem, CancellationToken cancellationToken) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = xmlReaderSettingsFactory.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; var list = new List(); - using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) + using (var fileStream = fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - cancellationToken.ThrowIfCancellationRequested(); + reader.MoveToContent(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (reader.Read()) { - switch (reader.Name) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) { - case "Banner": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Banner": { - AddImage(subtree, list, seasonNumber); + using (var subtree = reader.ReadSubtree()) + { + AddImage(subtree, list, seasonNumber); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index b3a84f304a..f6cdacd424 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -19,6 +19,7 @@ using System.Xml; using MediaBrowser.Common.IO; using MediaBrowser.Controller.IO; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -28,12 +29,14 @@ namespace MediaBrowser.Providers.TV private readonly IHttpClient _httpClient; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IFileSystem _fileSystem; + private readonly IXmlReaderSettingsFactory _xmlReaderSettingsFactory; - public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) + public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlReaderSettingsFactory) { _config = config; _httpClient = httpClient; _fileSystem = fileSystem; + _xmlReaderSettingsFactory = xmlReaderSettingsFactory; } public string Name @@ -75,7 +78,7 @@ namespace MediaBrowser.Providers.TV { var seriesOffset = TvdbSeriesProvider.GetSeriesOffset(item.ProviderIds); if (seriesOffset != null && seriesOffset.Value != 0) - return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, cancellationToken); + return TvdbSeasonImageProvider.GetImages(path, language, seriesOffset.Value + 1, _xmlReaderSettingsFactory, _fileSystem, cancellationToken); return GetImages(path, language, cancellationToken); } @@ -83,7 +86,7 @@ namespace MediaBrowser.Providers.TV { // No tvdb data yet. Don't blow up } - catch (DirectoryNotFoundException) + catch (IOException) { // No tvdb data yet. Don't blow up } @@ -94,43 +97,44 @@ namespace MediaBrowser.Providers.TV private IEnumerable GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = _xmlReaderSettingsFactory.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; var list = new List(); - using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) + using (var fileStream = _fileSystem.GetFileStream(xmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - cancellationToken.ThrowIfCancellationRequested(); + reader.MoveToContent(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (reader.Read()) { - switch (reader.Name) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) { - case "Banner": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Banner": { - AddImage(subtree, list); + using (var subtree = reader.ReadSubtree()) + { + AddImage(subtree, list); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index afa223f63c..3618e4ddc2 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -20,9 +20,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using MediaBrowser.Model.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -41,8 +40,10 @@ namespace MediaBrowser.Providers.TV private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IXmlReaderSettingsFactory _xmlSettings; + private readonly ILocalizationManager _localizationManager; - public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider) + public TvdbSeriesProvider(IZipClient zipClient, IHttpClient httpClient, IFileSystem fileSystem, IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IMemoryStreamProvider memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) { _zipClient = zipClient; _httpClient = httpClient; @@ -51,6 +52,8 @@ namespace MediaBrowser.Providers.TV _logger = logger; _libraryManager = libraryManager; _memoryStreamProvider = memoryStreamProvider; + _xmlSettings = xmlSettings; + _localizationManager = localizationManager; Current = this; } @@ -252,7 +255,8 @@ namespace MediaBrowser.Providers.TV } // Sanitize all files, except for extracted episode files - foreach (var file in Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.AllDirectories).ToList() + 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); @@ -281,20 +285,78 @@ namespace MediaBrowser.Providers.TV }).ConfigureAwait(false)) { - var doc = new XmlDocument(); - doc.Load(result); + return FindSeriesId(result); + } + } - if (doc.HasChildNodes) + private string FindSeriesId(Stream stream) + { + using (var streamReader = new StreamReader(stream, Encoding.UTF8)) + { + 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)) { - var node = doc.SelectSingleNode("//Series/seriesid"); + reader.MoveToContent(); - if (node != null) + // Loop through each element + while (reader.Read()) { - var idResult = node.InnerText; + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Series": + { + using (var subtree = reader.ReadSubtree()) + { + return FindSeriesId(subtree); + } + } - _logger.Info("Tvdb GetSeriesByRemoteId produced id of {0}", idResult ?? string.Empty); + default: + reader.Skip(); + break; + } + } + } + } + } - return idResult; + return null; + } + + private string FindSeriesId(XmlReader reader) + { + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "seriesid": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + return val; + } + + return null; + } + + default: + reader.Skip(); + break; } } } @@ -402,11 +464,11 @@ namespace MediaBrowser.Providers.TV } return true; } - catch (DirectoryNotFoundException) + catch (FileNotFoundException) { return false; } - catch (FileNotFoundException) + catch (IOException) { return false; } @@ -570,10 +632,10 @@ namespace MediaBrowser.Providers.TV /// /// The name. /// System.String. - internal static string GetComparableName(string name) + private string GetComparableName(string name) { name = name.ToLower(); - name = name.Normalize(NormalizationForm.FormKD); + name = _localizationManager.NormalizeFormKD(name); var sb = new StringBuilder(); foreach (var c in name) { @@ -615,58 +677,59 @@ namespace MediaBrowser.Providers.TV private void FetchSeriesInfo(MetadataResult result, string seriesXmlPath, CancellationToken cancellationToken) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = _xmlSettings.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; var episiodeAirDates = new List(); - using (var streamReader = new StreamReader(seriesXmlPath, Encoding.UTF8)) + using (var fileStream = _fileSystem.GetFileStream(seriesXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - cancellationToken.ThrowIfCancellationRequested(); + reader.MoveToContent(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (reader.Read()) { - switch (reader.Name) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) { - case "Series": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Series": { - FetchDataFromSeriesNode(result, subtree, cancellationToken); - } - break; - } - - case "Episode": - { - using (var subtree = reader.ReadSubtree()) - { - var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); - - if (date.HasValue) + using (var subtree = reader.ReadSubtree()) { - episiodeAirDates.Add(date.Value); + FetchDataFromSeriesNode(result, subtree, cancellationToken); } + break; } - break; - } - default: - reader.Skip(); - break; + case "Episode": + { + using (var subtree = reader.ReadSubtree()) + { + var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); + + if (date.HasValue) + { + episiodeAirDates.Add(date.Value); + } + } + break; + } + + default: + reader.Skip(); + break; + } } } } @@ -751,39 +814,40 @@ namespace MediaBrowser.Providers.TV /// The actors XML path. private void FetchActors(MetadataResult result, string actorsXmlPath) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = _xmlSettings.Create(false); - using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8)) + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; + + using (var fileStream = _fileSystem.GetFileStream(actorsXmlPath, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - if (reader.NodeType == XmlNodeType.Element) + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) { - switch (reader.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Actor": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Actor": { - FetchDataFromActorNode(result, subtree); + using (var subtree = reader.ReadSubtree()) + { + FetchDataFromActorNode(result, subtree); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } } } } @@ -1112,39 +1176,40 @@ namespace MediaBrowser.Providers.TV /// Task. private async Task ExtractEpisodes(string seriesDataPath, string xmlFile, long? lastTvDbUpdateTime) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = _xmlSettings.Create(false); - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; + + using (var fileStream = _fileSystem.GetFileStream(xmlFile, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, settings)) + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - if (reader.NodeType == XmlNodeType.Element) + reader.MoveToContent(); + + // Loop through each element + while (reader.Read()) { - switch (reader.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Episode": - { - var outerXml = reader.ReadOuterXml(); + switch (reader.Name) + { + case "Episode": + { + var outerXml = reader.ReadOuterXml(); - await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false); + await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false); + break; + } + + default: + reader.Skip(); break; - } - - default: - reader.Skip(); - break; + } } } } @@ -1154,13 +1219,11 @@ namespace MediaBrowser.Providers.TV private async Task SaveEpsiodeXml(string seriesDataPath, string xml, long? lastTvDbUpdateTime) { - var settings = new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - }; + var settings = _xmlSettings.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; var seasonNumber = -1; var episodeNumber = -1; @@ -1253,13 +1316,16 @@ namespace MediaBrowser.Providers.TV // Only save the file if not already there, or if the episode has changed if (hasEpisodeChanged || !_fileSystem.FileExists(file)) { - using (var writer = XmlWriter.Create(file, new XmlWriterSettings + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) { - Encoding = Encoding.UTF8, - Async = true - })) - { - await writer.WriteRawAsync(xml).ConfigureAwait(false); + using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings + { + Encoding = Encoding.UTF8, + Async = true + })) + { + await writer.WriteRawAsync(xml).ConfigureAwait(false); + } } } @@ -1270,13 +1336,16 @@ namespace MediaBrowser.Providers.TV // Only save the file if not already there, or if the episode has changed if (hasEpisodeChanged || !_fileSystem.FileExists(file)) { - using (var writer = XmlWriter.Create(file, new XmlWriterSettings + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, true)) { - Encoding = Encoding.UTF8, - Async = true - })) - { - await writer.WriteRawAsync(xml).ConfigureAwait(false); + using (var writer = XmlWriter.Create(fileStream, new XmlWriterSettings + { + Encoding = Encoding.UTF8, + Async = true + })) + { + await writer.WriteRawAsync(xml).ConfigureAwait(false); + } } } } @@ -1339,7 +1408,7 @@ namespace MediaBrowser.Providers.TV _fileSystem.DeleteFile(file); } } - catch (DirectoryNotFoundException) + catch (IOException) { // No biggie } diff --git a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs index 0b1878c060..e4db98b3fc 100644 --- a/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs +++ b/MediaBrowser.Server.Implementations/Localization/LocalizationManager.cs @@ -118,6 +118,11 @@ namespace MediaBrowser.Server.Implementations.Localization ).Normalize(NormalizationForm.FormC); } + public string NormalizeFormKD(string text) + { + return text.Normalize(NormalizationForm.FormKD); + } + /// /// Gets the cultures. /// diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 23d373ac45..71e964eec6 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -278,6 +278,7 @@ + @@ -383,6 +384,10 @@ {7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B} MediaBrowser.Model + + {442b5058-dcaf-4263-bb6a-f21e31120a1b} + MediaBrowser.Providers + {d7453b88-2266-4805-b39b-2b5a2a33e1ba} Mono.Nat diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs similarity index 97% rename from MediaBrowser.Providers/TV/SeriesPostScanTask.cs rename to MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs index 018f4aa944..f4ee3e1afc 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Server.Implementations/TV/SeriesPostScanTask.cs @@ -1,23 +1,22 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using MediaBrowser.Common.IO; -using MediaBrowser.Model.IO; +using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Plugins; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; using MediaBrowser.Model.Tasks; +using MediaBrowser.Providers.TV; -namespace MediaBrowser.Providers.TV +namespace MediaBrowser.Server.Implementations.TV { class SeriesGroup : List, IGrouping {