diff options
Diffstat (limited to 'MediaBrowser.Providers')
95 files changed, 2430 insertions, 2822 deletions
diff --git a/MediaBrowser.Providers/Books/AudioBookMetadataService.cs b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs new file mode 100644 index 000000000..696619a8c --- /dev/null +++ b/MediaBrowser.Providers/Books/AudioBookMetadataService.cs @@ -0,0 +1,41 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Manager; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.Providers.Books +{ + public class AudioBookMetadataService : MetadataService<AudioBook, SongInfo> + { + protected override void MergeData(MetadataResult<AudioBook> source, MetadataResult<AudioBook> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + + var sourceItem = source.Item; + var targetItem = target.Item; + + if (replaceData || targetItem.Artists.Count == 0) + { + targetItem.Artists = sourceItem.Artists.ToList(); + } + + if (replaceData || string.IsNullOrEmpty(targetItem.Album)) + { + targetItem.Album = sourceItem.Album; + } + } + + public AudioBookMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + } +} diff --git a/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs new file mode 100644 index 000000000..86b2cf1b1 --- /dev/null +++ b/MediaBrowser.Providers/Books/AudioPodcastMetadataService.cs @@ -0,0 +1,41 @@ +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Manager; +using System.Collections.Generic; +using System.Linq; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; + +namespace MediaBrowser.Providers.Books +{ + public class AudioPodcastMetadataService : MetadataService<AudioPodcast, SongInfo> + { + protected override void MergeData(MetadataResult<AudioPodcast> source, MetadataResult<AudioPodcast> target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + + var sourceItem = source.Item; + var targetItem = target.Item; + + if (replaceData || targetItem.Artists.Count == 0) + { + targetItem.Artists = sourceItem.Artists.ToList(); + } + + if (replaceData || string.IsNullOrEmpty(targetItem.Album)) + { + targetItem.Album = sourceItem.Album; + } + } + + public AudioPodcastMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem, IUserDataManager userDataManager, ILibraryManager libraryManager) : base(serverConfigurationManager, logger, providerManager, fileSystem, userDataManager, libraryManager) + { + } + } +} diff --git a/MediaBrowser.Providers/Books/BookMetadataService.cs b/MediaBrowser.Providers/Books/BookMetadataService.cs index 6f4a744c2..ccc69eb62 100644 --- a/MediaBrowser.Providers/Books/BookMetadataService.cs +++ b/MediaBrowser.Providers/Books/BookMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Books { diff --git a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs index 2dacb16ca..a625b93b8 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetMetadataService.cs @@ -9,7 +9,9 @@ using MediaBrowser.Providers.Manager; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.BoxSets { diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs index 2dce13ebc..852feab37 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetImageProvider.cs @@ -162,8 +162,7 @@ namespace MediaBrowser.Providers.BoxSets return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs index ab05c959e..547420092 100644 --- a/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs +++ b/MediaBrowser.Providers/BoxSets/MovieDbBoxSetProvider.cs @@ -3,7 +3,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -17,7 +16,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.BoxSets { @@ -317,8 +319,7 @@ namespace MediaBrowser.Providers.BoxSets return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs index 22e196d72..c2a6c07f9 100644 --- a/MediaBrowser.Providers/Channels/ChannelMetadataService.cs +++ b/MediaBrowser.Providers/Channels/ChannelMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Channels { diff --git a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs index 2f534c12e..c6013a94d 100644 --- a/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/CollectionFolderMetadataService.cs @@ -1,8 +1,10 @@ using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; diff --git a/MediaBrowser.Providers/Folders/FolderMetadataService.cs b/MediaBrowser.Providers/Folders/FolderMetadataService.cs index 8c4737fc4..94125b29d 100644 --- a/MediaBrowser.Providers/Folders/FolderMetadataService.cs +++ b/MediaBrowser.Providers/Folders/FolderMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Folders { diff --git a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs index b8f58307a..b1410bfd9 100644 --- a/MediaBrowser.Providers/Folders/UserViewMetadataService.cs +++ b/MediaBrowser.Providers/Folders/UserViewMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Folders { diff --git a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs b/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs deleted file mode 100644 index b26f23715..000000000 --- a/MediaBrowser.Providers/GameGenres/GameGenreImageProvider.cs +++ /dev/null @@ -1,144 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.Genres; -using MediaBrowser.Providers.ImagesByName; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CommonIO; - -namespace MediaBrowser.Providers.GameGenres -{ - public class GameGenreImageProvider : IRemoteImageProvider - { - private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - - private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); - - public GameGenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) - { - _config = config; - _httpClient = httpClient; - _fileSystem = fileSystem; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "Emby Designs"; } - } - - public bool Supports(IHasImages item) - { - return item is GameGenre; - } - - public IEnumerable<ImageType> GetSupportedImages(IHasImages item) - { - return new List<ImageType> - { - ImageType.Primary, - ImageType.Thumb - }; - } - - public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken) - { - return GetImages(item, true, true, cancellationToken); - } - - private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken) - { - var list = new List<RemoteImageInfo>(); - - if (posters) - { - var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenreposters.txt"); - - await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false); - - list.Add(GetImage(item, posterPath, ImageType.Primary, "folder")); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (thumbs) - { - var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegamegenrethumbs.txt"); - - await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false); - - list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb")); - } - - return list.Where(i => i != null); - } - - private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename) - { - var list = ImageUtils.GetAvailableImages(filename); - - var match = ImageUtils.FindMatch(item, list); - - if (!string.IsNullOrEmpty(match)) - { - var url = GetUrl(match, remoteFilename); - - return new RemoteImageInfo - { - ProviderName = Name, - Type = type, - Url = url - }; - } - - return null; - } - - private string GetUrl(string image, string filename) - { - return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenres/{0}/{1}.jpg", image, filename); - } - - private Task EnsureThumbsList(string file, CancellationToken cancellationToken) - { - const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenrethumbs.txt"; - - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); - } - - private Task EnsurePosterList(string file, CancellationToken cancellationToken) - { - const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/gamegenreposters.txt"; - - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); - } - - public int Order - { - get { return 0; } - } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); - } - } -} diff --git a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs index fb2244e32..180d36378 100644 --- a/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs +++ b/MediaBrowser.Providers/GameGenres/GameGenreMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.GameGenres { diff --git a/MediaBrowser.Providers/Games/GameMetadataService.cs b/MediaBrowser.Providers/Games/GameMetadataService.cs index a44f1d95f..d6f61fa94 100644 --- a/MediaBrowser.Providers/Games/GameMetadataService.cs +++ b/MediaBrowser.Providers/Games/GameMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Games { diff --git a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs index 6cf2a45a4..5794e8c9b 100644 --- a/MediaBrowser.Providers/Games/GameSystemMetadataService.cs +++ b/MediaBrowser.Providers/Games/GameSystemMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Games { diff --git a/MediaBrowser.Providers/Genres/GenreImageProvider.cs b/MediaBrowser.Providers/Genres/GenreImageProvider.cs deleted file mode 100644 index 954cd008e..000000000 --- a/MediaBrowser.Providers/Genres/GenreImageProvider.cs +++ /dev/null @@ -1,143 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using MediaBrowser.Providers.ImagesByName; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CommonIO; - -namespace MediaBrowser.Providers.Genres -{ - public class GenreImageProvider : IRemoteImageProvider - { - private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; - private readonly IFileSystem _fileSystem; - - private readonly SemaphoreSlim _listResourcePool = new SemaphoreSlim(1, 1); - - public GenreImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) - { - _config = config; - _httpClient = httpClient; - _fileSystem = fileSystem; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "Emby Designs"; } - } - - public bool Supports(IHasImages item) - { - return item is Genre; - } - - public IEnumerable<ImageType> GetSupportedImages(IHasImages item) - { - return new List<ImageType> - { - ImageType.Primary, - ImageType.Thumb - }; - } - - public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken) - { - return GetImages(item, true, true, cancellationToken); - } - - private async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, bool posters, bool thumbs, CancellationToken cancellationToken) - { - var list = new List<RemoteImageInfo>(); - - if (posters) - { - var posterPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegenreposters.txt"); - - await EnsurePosterList(posterPath, cancellationToken).ConfigureAwait(false); - - list.Add(GetImage(item, posterPath, ImageType.Primary, "folder")); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (thumbs) - { - var thumbsPath = Path.Combine(_config.ApplicationPaths.CachePath, "imagesbyname", "remotegenrethumbs.txt"); - - await EnsureThumbsList(thumbsPath, cancellationToken).ConfigureAwait(false); - - list.Add(GetImage(item, thumbsPath, ImageType.Thumb, "thumb")); - } - - return list.Where(i => i != null); - } - - private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename) - { - var list = ImageUtils.GetAvailableImages(filename); - - var match = ImageUtils.FindMatch(item, list); - - if (!string.IsNullOrEmpty(match)) - { - var url = GetUrl(match, remoteFilename); - - return new RemoteImageInfo - { - ProviderName = Name, - Type = type, - Url = url - }; - } - - return null; - } - - private string GetUrl(string image, string filename) - { - return string.Format("https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genres/{0}/{1}.jpg", image, filename); - } - - private Task EnsureThumbsList(string file, CancellationToken cancellationToken) - { - const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genrethumbs.txt"; - - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); - } - - private Task EnsurePosterList(string file, CancellationToken cancellationToken) - { - const string url = "https://raw.github.com/MediaBrowser/MediaBrowser.Resources/master/images/imagesbyname/genreposters.txt"; - - return ImageUtils.EnsureList(url, file, _httpClient, _fileSystem, _listResourcePool, cancellationToken); - } - - public int Order - { - get { return 0; } - } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - BufferContent = false - }); - } - } -} diff --git a/MediaBrowser.Providers/Genres/GenreMetadataService.cs b/MediaBrowser.Providers/Genres/GenreMetadataService.cs index d4ea3e9cf..39f9354db 100644 --- a/MediaBrowser.Providers/Genres/GenreMetadataService.cs +++ b/MediaBrowser.Providers/Genres/GenreMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Genres { diff --git a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs index b53348749..bbcbbda90 100644 --- a/MediaBrowser.Providers/ImagesByName/ImageUtils.cs +++ b/MediaBrowser.Providers/ImagesByName/ImageUtils.cs @@ -6,7 +6,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.ImagesByName { @@ -68,23 +70,26 @@ namespace MediaBrowser.Providers.ImagesByName .Replace("/", string.Empty); } - public static IEnumerable<string> GetAvailableImages(string file) + public static IEnumerable<string> GetAvailableImages(string file, IFileSystem fileSystem) { - using (var reader = new StreamReader(file)) + using (var fileStream = fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { - var lines = new List<string>(); - - while (!reader.EndOfStream) + using (var reader = new StreamReader(fileStream)) { - var text = reader.ReadLine(); + var lines = new List<string>(); - if (!string.IsNullOrWhiteSpace(text)) + while (!reader.EndOfStream) { - lines.Add(text); + var text = reader.ReadLine(); + + if (!string.IsNullOrWhiteSpace(text)) + { + lines.Add(text); + } } - } - return lines; + return lines; + } } } } diff --git a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs index 1d99a678b..df4a03cdb 100644 --- a/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs +++ b/MediaBrowser.Providers/LiveTv/AudioRecordingService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.LiveTv { diff --git a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs index 8abb99689..7b5065986 100644 --- a/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/ChannelMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.LiveTv { diff --git a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs index b73d82c19..eaacc41fa 100644 --- a/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs +++ b/MediaBrowser.Providers/LiveTv/ProgramMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.LiveTv { diff --git a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs index 15530f8f9..a8df3c88b 100644 --- a/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs +++ b/MediaBrowser.Providers/LiveTv/VideoRecordingService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.LiveTv { diff --git a/MediaBrowser.Providers/Manager/ImageSaver.cs b/MediaBrowser.Providers/Manager/ImageSaver.cs index 767c034ee..5146df6e6 100644 --- a/MediaBrowser.Providers/Manager/ImageSaver.cs +++ b/MediaBrowser.Providers/Manager/ImageSaver.cs @@ -16,7 +16,8 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Manager { @@ -38,7 +39,7 @@ namespace MediaBrowser.Providers.Manager private readonly ILibraryMonitor _libraryMonitor; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// <summary> /// Initializes a new instance of the <see cref="ImageSaver" /> class. @@ -47,7 +48,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="libraryMonitor">The directory watchers.</param> /// <param name="fileSystem">The file system.</param> /// <param name="logger">The logger.</param> - public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamProvider memoryStreamProvider) + public ImageSaver(IServerConfigurationManager config, ILibraryMonitor libraryMonitor, IFileSystem fileSystem, ILogger logger, IMemoryStreamFactory memoryStreamProvider) { _config = config; _libraryMonitor = libraryMonitor; @@ -172,14 +173,14 @@ namespace MediaBrowser.Providers.Manager try { - var currentFile = new FileInfo(currentPath); + var currentFile = _fileSystem.GetFileInfo(currentPath); // This will fail if the file is hidden if (currentFile.Exists) { - if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + if (currentFile.IsHidden) { - currentFile.Attributes &= ~FileAttributes.Hidden; + _fileSystem.SetHidden(currentFile.FullName, false); } _fileSystem.DeleteFile(currentFile.FullName); @@ -254,18 +255,22 @@ namespace MediaBrowser.Providers.Manager _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); // If the file is currently hidden we'll have to remove that or the save will fail - var file = new FileInfo(path); + var file = _fileSystem.GetFileInfo(path); // This will fail if the file is hidden if (file.Exists) { - if ((file.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + if (file.IsHidden) { - file.Attributes &= ~FileAttributes.Hidden; + _fileSystem.SetHidden(file.FullName, false); + } + if (file.IsReadOnly) + { + _fileSystem.SetReadOnly(path, false); } } - using (var fs = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await source.CopyToAsync(fs, StreamDefaults.DefaultCopyToBufferSize, cancellationToken) .ConfigureAwait(false); @@ -273,10 +278,7 @@ namespace MediaBrowser.Providers.Manager if (_config.Configuration.SaveMetadataHidden) { - file.Refresh(); - - // Add back the attribute - file.Attributes |= FileAttributes.Hidden; + _fileSystem.SetHidden(file.FullName, true); } } finally diff --git a/MediaBrowser.Providers/Manager/ItemImageProvider.cs b/MediaBrowser.Providers/Manager/ItemImageProvider.cs index 898fa522d..9dff243c1 100644 --- a/MediaBrowser.Providers/Manager/ItemImageProvider.cs +++ b/MediaBrowser.Providers/Manager/ItemImageProvider.cs @@ -15,8 +15,12 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Controller.Entities.Audio; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Model.MediaInfo; @@ -154,7 +158,7 @@ namespace MediaBrowser.Providers.Manager { var mimeType = MimeTypes.GetMimeType(response.Path); - var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true); + var stream = _fileSystem.GetFileStream(response.Path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true); await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false); } @@ -370,14 +374,14 @@ namespace MediaBrowser.Providers.Manager } // Delete the source file - var currentFile = new FileInfo(image.Path); + var currentFile = _fileSystem.GetFileInfo(image.Path); // Deletion will fail if the file is hidden so remove the attribute first if (currentFile.Exists) { - if ((currentFile.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden) + if (currentFile.IsHidden) { - currentFile.Attributes &= ~FileAttributes.Hidden; + _fileSystem.SetHidden(currentFile.FullName, false); } _fileSystem.DeleteFile(currentFile.FullName); @@ -552,9 +556,7 @@ namespace MediaBrowser.Providers.Manager switch (type) { case ImageType.Primary: - return false; - case ImageType.Thumb: - return false; + return !(item is Movie || item is Series || item is Game); default: return true; } @@ -611,7 +613,7 @@ namespace MediaBrowser.Providers.Manager { try { - if (item.GetImages(imageType).Any(i => new FileInfo(i.Path).Length == response.ContentLength.Value)) + if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value)) { response.Content.Dispose(); continue; diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index 41cacbe0a..9c6d6a482 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -10,7 +10,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Providers; @@ -78,16 +78,15 @@ namespace MediaBrowser.Providers.Manager bool hasRefreshedMetadata = true; bool hasRefreshedImages = true; + var isFirstRefresh = item.DateLastRefreshed == default(DateTime); // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { - var providers = GetProviders(item, refreshOptions, requiresRefresh) + var providers = GetProviders(item, refreshOptions, isFirstRefresh, requiresRefresh) .ToList(); - var dateLastRefresh = item.DateLastRefreshed; - - if (providers.Count > 0 || dateLastRefresh == default(DateTime)) + if (providers.Count > 0 || isFirstRefresh) { if (item.BeforeMetadataRefresh()) { @@ -110,11 +109,7 @@ namespace MediaBrowser.Providers.Manager var result = await RefreshWithProviders(metadataResult, id, refreshOptions, providers, itemImageProvider, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; - if (result.Failures == 0) - { - hasRefreshedMetadata = true; - } - else + if (result.Failures > 0) { hasRefreshedMetadata = false; } @@ -138,19 +133,13 @@ namespace MediaBrowser.Providers.Manager var result = await itemImageProvider.RefreshImages(itemOfType, libraryOptions, providers, refreshOptions, config, cancellationToken).ConfigureAwait(false); updateType = updateType | result.UpdateType; - if (result.Failures == 0) - { - hasRefreshedImages = true; - } - else + if (result.Failures > 0) { hasRefreshedImages = false; } } } - var isFirstRefresh = item.DateLastRefreshed == default(DateTime); - var beforeSaveResult = await BeforeSave(itemOfType, isFirstRefresh || refreshOptions.ReplaceAllMetadata || refreshOptions.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || requiresRefresh, updateType).ConfigureAwait(false); updateType = updateType | beforeSaveResult; @@ -373,15 +362,18 @@ namespace MediaBrowser.Providers.Manager /// Gets the providers. /// </summary> /// <returns>IEnumerable{`0}.</returns> - protected IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool requiresRefresh) + protected IEnumerable<IMetadataProvider> GetProviders(IHasMetadata item, MetadataRefreshOptions options, bool isFirstRefresh, bool requiresRefresh) { // Get providers to refresh var providers = ((ProviderManager)ProviderManager).GetMetadataProviders<TItemType>(item).ToList(); - var dateLastRefresh = item.DateLastRefreshed; + var metadataRefreshMode = options.MetadataRefreshMode; // Run all if either of these flags are true - var runAllProviders = options.ReplaceAllMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || dateLastRefresh == default(DateTime) || requiresRefresh; + var runAllProviders = options.ReplaceAllMetadata || + metadataRefreshMode == MetadataRefreshMode.FullRefresh || + (isFirstRefresh && metadataRefreshMode >= MetadataRefreshMode.Default) || + (requiresRefresh && metadataRefreshMode >= MetadataRefreshMode.Default); if (!runAllProviders) { @@ -404,6 +396,9 @@ namespace MediaBrowser.Providers.Manager } else { + var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>() + .Any(); + providers = providers.Where(i => { // If any provider reports a change, always run local ones as well @@ -412,12 +407,14 @@ namespace MediaBrowser.Providers.Manager return true; } - var anyRemoteProvidersChanged = providersWithChanges.OfType<IRemoteMetadataProvider>() - .Any(); - // If any remote providers changed, run them all so that priorities can be honored if (i is IRemoteMetadataProvider) { + if (options.MetadataRefreshMode == MetadataRefreshMode.ValidationOnly) + { + return false; + } + return anyRemoteProvidersChanged; } @@ -536,7 +533,7 @@ namespace MediaBrowser.Providers.Manager refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; // Only one local provider allowed per item - if (item.IsLocked || IsFullLocalMetadata(localItem.Item)) + if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item)) { hasLocalMetadata = true; } @@ -573,14 +570,16 @@ namespace MediaBrowser.Providers.Manager { if (refreshResult.UpdateType > ItemUpdateType.None) { - // If no local metadata, take data from item itself - if (!hasLocalMetadata) + if (hasLocalMetadata) + { + MergeData(temp, metadata, item.LockedFields, true, true); + } + else { // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields - MergeData(metadata, temp, new List<MetadataFields>(), false, true); + MergeData(metadata, temp, new List<MetadataFields>(), false, false); + MergeData(temp, metadata, item.LockedFields, true, false); } - - MergeData(temp, metadata, item.LockedFields, true, true); } } diff --git a/MediaBrowser.Providers/Manager/ProviderManager.cs b/MediaBrowser.Providers/Manager/ProviderManager.cs index dfeceed7d..a2f3d0459 100644 --- a/MediaBrowser.Providers/Manager/ProviderManager.cs +++ b/MediaBrowser.Providers/Manager/ProviderManager.cs @@ -19,8 +19,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Manager @@ -64,7 +66,7 @@ namespace MediaBrowser.Providers.Manager private IExternalId[] _externalIds; private readonly Func<ILibraryManager> _libraryManagerFactory; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; /// <summary> /// Initializes a new instance of the <see cref="ProviderManager" /> class. @@ -74,7 +76,7 @@ namespace MediaBrowser.Providers.Manager /// <param name="libraryMonitor">The directory watchers.</param> /// <param name="logManager">The log manager.</param> /// <param name="fileSystem">The file system.</param> - public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json, IMemoryStreamProvider memoryStreamProvider) + public ProviderManager(IHttpClient httpClient, IServerConfigurationManager configurationManager, ILibraryMonitor libraryMonitor, ILogManager logManager, IFileSystem fileSystem, IServerApplicationPaths appPaths, Func<ILibraryManager> libraryManagerFactory, IJsonSerializer json, IMemoryStreamFactory memoryStreamProvider) { _logger = logManager.GetLogger("ProviderManager"); _httpClient = httpClient; @@ -127,7 +129,8 @@ namespace MediaBrowser.Providers.Manager { CancellationToken = cancellationToken, ResourcePool = resourcePool, - Url = url + Url = url, + BufferContent = false }).ConfigureAwait(false); @@ -147,7 +150,7 @@ namespace MediaBrowser.Providers.Manager throw new ArgumentNullException("source"); } - var fileStream = _fileSystem.GetFileStream(source, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true); + var fileStream = _fileSystem.GetFileStream(source, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.ReadWrite, true); return new ImageSaver(ConfigurationManager, _libraryMonitor, _fileSystem, _logger, _memoryStreamProvider).SaveImage(item, fileStream, mimeType, type, imageIndex, saveLocallyWithMedia, cancellationToken); } @@ -269,17 +272,17 @@ namespace MediaBrowser.Providers.Manager { var options = GetMetadataOptions(item); - return GetMetadataProvidersInternal<T>(item, options, false, true); + return GetMetadataProvidersInternal<T>(item, options, false, false, true); } - private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem) + private IEnumerable<IMetadataProvider<T>> GetMetadataProvidersInternal<T>(IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem) where T : IHasMetadata { // Avoid implicitly captured closure var currentOptions = options; return _metadataProviders.OfType<IMetadataProvider<T>>() - .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, checkIsOwnedItem)) + .Where(i => CanRefresh(i, item, currentOptions, includeDisabled, forceEnableInternetMetadata, checkIsOwnedItem)) .OrderBy(i => GetConfiguredOrder(i, options)) .ThenBy(GetDefaultOrder); } @@ -291,7 +294,7 @@ namespace MediaBrowser.Providers.Manager return GetImageProviders(item, options, new ImageRefreshOptions(new DirectoryService(_logger, _fileSystem)), includeDisabled).OfType<IRemoteImageProvider>(); } - private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool checkIsOwnedItem) + private bool CanRefresh(IMetadataProvider provider, IHasMetadata item, MetadataOptions options, bool includeDisabled, bool forceEnableInternetMetadata, bool checkIsOwnedItem) { if (!includeDisabled) { @@ -303,7 +306,7 @@ namespace MediaBrowser.Providers.Manager if (provider is IRemoteMetadataProvider) { - if (!item.IsInternetMetadataEnabled()) + if (!forceEnableInternetMetadata && !item.IsInternetMetadataEnabled()) { return false; } @@ -354,7 +357,7 @@ namespace MediaBrowser.Providers.Manager if (provider is IRemoteImageProvider) { - if (!item.IsInternetMetadataEnabled()) + if (!refreshOptions.ForceEnableInternetMetadata && !item.IsInternetMetadataEnabled()) { return false; } @@ -498,7 +501,7 @@ namespace MediaBrowser.Providers.Manager private void AddMetadataPlugins<T>(List<MetadataPlugin> list, T item, MetadataOptions options) where T : IHasMetadata { - var providers = GetMetadataProvidersInternal<T>(item, options, true, false).ToList(); + var providers = GetMetadataProvidersInternal<T>(item, options, true, false, false).ToList(); // Locals list.AddRange(providers.Where(i => (i is ILocalMetadataProvider)).Select(i => new MetadataPlugin @@ -558,8 +561,6 @@ namespace MediaBrowser.Providers.Manager new MetadataOptions(); } - private readonly ConcurrentDictionary<string, SemaphoreSlim> _fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>(); - /// <summary> /// Saves the metadata. /// </summary> @@ -583,6 +584,7 @@ namespace MediaBrowser.Providers.Manager return SaveMetadata(item, updateType, _savers.Where(i => savers.Contains(i.Name, StringComparer.OrdinalIgnoreCase))); } + private readonly SemaphoreSlim _saveLock = new SemaphoreSlim(1,1); /// <summary> /// Saves the metadata. /// </summary> @@ -612,9 +614,7 @@ namespace MediaBrowser.Providers.Manager continue; } - var semaphore = _fileLocks.GetOrAdd(path, key => new SemaphoreSlim(1, 1)); - - await semaphore.WaitAsync().ConfigureAwait(false); + await _saveLock.WaitAsync().ConfigureAwait(false); try { @@ -627,8 +627,8 @@ namespace MediaBrowser.Providers.Manager } finally { + _saveLock.Release(); _libraryMonitor.ReportFileSystemChangeComplete(path, false); - semaphore.Release(); } } else @@ -715,7 +715,7 @@ namespace MediaBrowser.Providers.Manager var options = GetMetadataOptions(dummy); - var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false) + var providers = GetMetadataProvidersInternal<TItemType>(dummy, options, searchInfo.IncludeDisabledProviders, false, false) .OfType<IRemoteSearchProvider<TLookupType>>(); if (!string.IsNullOrEmpty(searchInfo.SearchProviderName)) @@ -861,8 +861,8 @@ namespace MediaBrowser.Providers.Manager private readonly ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>> _refreshQueue = new ConcurrentQueue<Tuple<Guid, MetadataRefreshOptions>>(); - 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) { @@ -872,38 +872,18 @@ namespace MediaBrowser.Providers.Manager } _refreshQueue.Enqueue(new Tuple<Guid, MetadataRefreshOptions>(id, options)); - StartRefreshTimer(); - } - - private void StartRefreshTimer() - { - if (_disposed) - { - return; - } - lock (_refreshTimerLock) + lock (_refreshQueueLock) { - 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<Guid, MetadataRefreshOptions> refreshItem; var libraryManager = _libraryManagerFactory(); @@ -937,7 +917,10 @@ namespace MediaBrowser.Providers.Manager } } - StopRefreshTimer(); + lock (_refreshQueueLock) + { + _isProcessingRefreshQueue = false; + } } private async Task RefreshItem(BaseItem item, MetadataRefreshOptions options, CancellationToken cancellationToken) @@ -1016,7 +999,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 8b3d1fb7e..fe554545f 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> <PropertyGroup> <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> @@ -13,6 +13,8 @@ <SolutionDir Condition="$(SolutionDir) == '' Or $(SolutionDir) == '*Undefined*'">..\</SolutionDir> <ProductVersion>10.0.0</ProductVersion> <SchemaVersion>2.0</SchemaVersion> + <ProjectTypeGuids>{786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids> + <TargetFrameworkProfile>Profile7</TargetFrameworkProfile> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> @@ -23,7 +25,6 @@ <DefineConstants>DEBUG;TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> <DebugType>none</DebugType> @@ -32,7 +33,6 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release Mono|AnyCPU' "> <DebugType>none</DebugType> @@ -41,47 +41,13 @@ <DefineConstants>TRACE</DefineConstants> <ErrorReport>prompt</ErrorReport> <WarningLevel>4</WarningLevel> - <TargetFrameworkVersion>v4.5</TargetFrameworkVersion> </PropertyGroup> <ItemGroup> - <Reference Include="BDInfo, Version=1.0.5167.21152, Culture=neutral, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\BDInfo.dll</HintPath> - </Reference> - <Reference Include="CommonIO, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\CommonIO.1.0.0.9\lib\net45\CommonIO.dll</HintPath> - </Reference> - <Reference Include="DvdLib, Version=1.0.5167.21152, Culture=neutral, PublicKeyToken=7a2f3f5ec8d93575, processorArchitecture=MSIL"> - <SpecificVersion>False</SpecificVersion> - <HintPath>..\packages\MediaBrowser.BdInfo.1.0.0.10\lib\net35\DvdLib.dll</HintPath> - </Reference> - <Reference Include="MoreLinq"> - <HintPath>..\packages\morelinq.1.4.0\lib\net35\MoreLinq.dll</HintPath> - </Reference> - <Reference Include="Patterns.Logging"> - <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath> - </Reference> - <Reference Include="policy.2.0.taglib-sharp"> - <HintPath>..\packages\taglib.2.1.0.0\lib\policy.2.0.taglib-sharp.dll</HintPath> - </Reference> - <Reference Include="System" /> - <Reference Include="System.Core" /> - <Reference Include="System.Net" /> - <Reference Include="System.Xml.Linq" /> - <Reference Include="System.Data.DataSetExtensions" /> - <Reference Include="Microsoft.CSharp" /> - <Reference Include="System.Data" /> - <Reference Include="System.Xml" /> - <Reference Include="taglib-sharp"> - <HintPath>..\packages\taglib.2.1.0.0\lib\taglib-sharp.dll</HintPath> - <Private>True</Private> - </Reference> - </ItemGroup> - <ItemGroup> <Compile Include="..\SharedVersion.cs"> <Link>Properties\SharedVersion.cs</Link> </Compile> + <Compile Include="Books\AudioBookMetadataService.cs" /> + <Compile Include="Books\AudioPodcastMetadataService.cs" /> <Compile Include="Books\BookMetadataService.cs" /> <Compile Include="BoxSets\BoxSetMetadataService.cs" /> <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" /> @@ -116,8 +82,6 @@ <Compile Include="Movies\MovieDbSearch.cs" /> <Compile Include="Movies\MovieMetadataService.cs" /> <Compile Include="Movies\TmdbSettings.cs" /> - <Compile Include="GameGenres\GameGenreImageProvider.cs" /> - <Compile Include="Genres\GenreImageProvider.cs" /> <Compile Include="ImagesByName\ImageUtils.cs" /> <Compile Include="MediaInfo\AudioImageProvider.cs" /> <Compile Include="MediaInfo\VideoImageProvider.cs" /> @@ -149,16 +113,12 @@ <Compile Include="People\PersonMetadataService.cs" /> <Compile Include="People\MovieDbPersonProvider.cs" /> <Compile Include="Photos\PhotoAlbumMetadataService.cs" /> - <Compile Include="Photos\PhotoHelper.cs" /> <Compile Include="Photos\PhotoMetadataService.cs" /> - <Compile Include="Photos\PhotoProvider.cs" /> <Compile Include="Playlists\PlaylistMetadataService.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Manager\ProviderUtils.cs" /> <Compile Include="Studios\StudiosImageProvider.cs" /> <Compile Include="Studios\StudioMetadataService.cs" /> - <Compile Include="Subtitles\ConfigurationExtension.cs" /> - <Compile Include="Subtitles\OpenSubtitleDownloader.cs" /> <Compile Include="Subtitles\SubtitleManager.cs" /> <Compile Include="TV\DummySeasonProvider.cs" /> <Compile Include="TV\EpisodeMetadataService.cs" /> @@ -180,7 +140,6 @@ <Compile Include="TV\SeasonMetadataService.cs" /> <Compile Include="TV\TheTVDB\TvdbEpisodeProvider.cs" /> <Compile Include="TV\TheTVDB\TvdbSeriesProvider.cs" /> - <Compile Include="TV\SeriesPostScanTask.cs" /> <Compile Include="TV\TheTVDB\TvdbPrescanTask.cs" /> <Compile Include="TV\TvExternalIds.cs" /> <Compile Include="Users\UserMetadataService.cs" /> @@ -188,6 +147,10 @@ <Compile Include="Years\YearMetadataService.cs" /> </ItemGroup> <ItemGroup> + <ProjectReference Include="..\DvdLib\DvdLib.csproj"> + <Project>{713f42b5-878e-499d-a878-e4c652b1d5e8}</Project> + <Name>DvdLib</Name> + </ProjectReference> <ProjectReference Include="..\MediaBrowser.Common\MediaBrowser.Common.csproj"> <Project>{9142EEFA-7570-41E1-BFCC-468BB571AF2F}</Project> <Name>MediaBrowser.Common</Name> @@ -200,16 +163,9 @@ <Project>{7EEEB4BB-F3E8-48FC-B4C5-70F0FFF8329B}</Project> <Name>MediaBrowser.Model</Name> </ProjectReference> - <ProjectReference Include="..\OpenSubtitlesHandler\OpenSubtitlesHandler.csproj"> - <Project>{4a4402d4-e910-443b-b8fc-2c18286a2ca0}</Project> - <Name>OpenSubtitlesHandler</Name> - </ProjectReference> - </ItemGroup> - <ItemGroup> - <None Include="packages.config" /> </ItemGroup> <ItemGroup /> - <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <Import Project="$(MSBuildExtensionsPath32)\Microsoft\Portable\$(TargetFrameworkVersion)\Microsoft.Portable.CSharp.targets" /> <!-- To modify your build process, add your task inside one of the targets below and uncomment it. Other similar extension points exist, see Microsoft.Common.targets. <Target Name="BeforeBuild"> diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets b/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets new file mode 100644 index 000000000..e69ce0e64 --- /dev/null +++ b/MediaBrowser.Providers/MediaBrowser.Providers.nuget.targets @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8" standalone="no"?> +<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Target Name="EmitMSBuildWarning" BeforeTargets="Build"> + <Warning Text="Packages containing MSBuild targets and props files cannot be fully installed in projects targeting multiple frameworks. The MSBuild targets and props files have been ignored." /> + </Target> +</Project>
\ No newline at end of file diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index 68fc80371..d65084287 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -6,13 +6,12 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.MediaInfo { @@ -21,8 +20,6 @@ namespace MediaBrowser.Providers.MediaInfo /// </summary> public class AudioImageProvider : IDynamicImageProvider, IHasItemChangeMonitor { - private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); - private readonly IMediaEncoder _mediaEncoder; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; @@ -65,41 +62,25 @@ namespace MediaBrowser.Providers.MediaInfo if (!_fileSystem.FileExists(path)) { - var semaphore = GetLock(path); - - // Acquire a lock - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - // Check again in case it was saved while waiting for the lock - if (!_fileSystem.FileExists(path)) - { - _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); - - var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ?? - imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ?? - imageStreams.FirstOrDefault(); + _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); - var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index; + var imageStream = imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("front", StringComparison.OrdinalIgnoreCase) != -1) ?? + imageStreams.FirstOrDefault(i => (i.Comment ?? string.Empty).IndexOf("cover", StringComparison.OrdinalIgnoreCase) != -1) ?? + imageStreams.FirstOrDefault(); - var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false); + var imageStreamIndex = imageStream == null ? (int?)null : imageStream.Index; - File.Copy(tempFile, path, true); + var tempFile = await _mediaEncoder.ExtractAudioImage(item.Path, imageStreamIndex, cancellationToken).ConfigureAwait(false); - try - { - File.Delete(tempFile); - } - catch - { + _fileSystem.CopyFile(tempFile, path, true); - } - } + try + { + _fileSystem.DeleteFile(tempFile); } - finally + catch { - semaphore.Release(); + } } @@ -143,16 +124,6 @@ namespace MediaBrowser.Providers.MediaInfo } } - /// <summary> - /// Gets the lock. - /// </summary> - /// <param name="filename">The filename.</param> - /// <returns>SemaphoreSlim.</returns> - private SemaphoreSlim GetLock(string filename) - { - return _locks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); - } - public string Name { get { return "Image Extractor"; } diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs index d255110fb..eaffc12d7 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeProvider.cs @@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; @@ -21,7 +20,10 @@ using System; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo { diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs index be0b2ca6d..0a070d348 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeVideoInfo.cs @@ -7,7 +7,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Providers; @@ -26,7 +25,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo { @@ -132,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<Model.MediaInfo.MediaInfo>(cachePath); - } - catch (FileNotFoundException) - { - - } - catch (DirectoryNotFoundException) - { - } - var protocol = item.LocationType == LocationType.Remote ? MediaProtocol.Http : MediaProtocol.File; @@ -218,7 +203,6 @@ namespace MediaBrowser.Providers.MediaInfo var videoStream = mediaStreams.FirstOrDefault(i => i.Type == MediaStreamType.Video); - video.VideoBitRate = videoStream == null ? null : videoStream.BitRate; video.DefaultVideoStreamIndex = videoStream == null ? (int?)null : videoStream.Index; video.HasSubtitles = mediaStreams.Any(i => i.Type == MediaStreamType.Subtitle); @@ -605,7 +589,7 @@ namespace MediaBrowser.Providers.MediaInfo private void FetchFromDvdLib(Video item, IIsoMount mount) { var path = mount == null ? item.Path : mount.MountedPath; - var dvd = new Dvd(path); + var dvd = new Dvd(path, _fileSystem); var primaryTitle = dvd.Titles.OrderByDescending(GetRuntime).FirstOrDefault(); @@ -653,7 +637,7 @@ namespace MediaBrowser.Providers.MediaInfo /// <returns>System.Nullable{IsoType}.</returns> 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/MediaInfo/SubtitleDownloader.cs b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs index 9ae8413d1..b69e37136 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleDownloader.cs @@ -139,7 +139,7 @@ namespace MediaBrowser.Providers.MediaInfo request.IndexNumberEnd = episode.IndexNumberEnd; request.SeriesName = episode.SeriesName; } - + try { var searchResults = await _subtitleManager.SearchSubtitles(request, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs index 024171f40..313feda52 100644 --- a/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs +++ b/MediaBrowser.Providers/MediaInfo/SubtitleResolver.cs @@ -1,13 +1,15 @@ using MediaBrowser.Model.Extensions; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using System; using System.Collections.Generic; using System.IO; using System.Linq; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.MediaInfo { @@ -43,6 +45,11 @@ namespace MediaBrowser.Providers.MediaInfo var codec = Path.GetExtension(fullName).ToLower().TrimStart('.'); + if (string.Equals(codec, "txt", StringComparison.OrdinalIgnoreCase)) + { + codec = "srt"; + } + // If the subtitle file matches the video file name if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) { @@ -72,9 +79,9 @@ namespace MediaBrowser.Providers.MediaInfo // Try to translate to three character code // Be flexible and check against both the full and three character versions var culture = _localization.GetCultures() - .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || - string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || - string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || + .FirstOrDefault(i => string.Equals(i.DisplayName, language, StringComparison.OrdinalIgnoreCase) || + string.Equals(i.Name, language, StringComparison.OrdinalIgnoreCase) || + string.Equals(i.ThreeLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase) || string.Equals(i.TwoLetterISOLanguageName, language, StringComparison.OrdinalIgnoreCase)); if (culture != null) @@ -117,7 +124,7 @@ namespace MediaBrowser.Providers.MediaInfo { get { - return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami" }; + return new[] { ".srt", ".ssa", ".ass", ".sub", ".smi", ".sami", ".txt" }; } } @@ -136,7 +143,7 @@ namespace MediaBrowser.Providers.MediaInfo return files.Where(i => { - if (!i.Attributes.HasFlag(FileAttributes.Directory) && + if (!i.IsDirectory && SubtitleExtensions.Contains(i.Extension, StringComparer.OrdinalIgnoreCase)) { var fileNameWithoutExtension = fileSystem.GetFileNameWithoutExtension(i); diff --git a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs index 45d1c3d80..ca701b70f 100644 --- a/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/VideoImageProvider.cs @@ -13,7 +13,6 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; namespace MediaBrowser.Providers.MediaInfo { diff --git a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs index 2a40e4d85..dd2cad1f9 100644 --- a/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs +++ b/MediaBrowser.Providers/Movies/FanartMovieImageProvider.cs @@ -19,7 +19,9 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Providers.TV; @@ -116,7 +118,7 @@ namespace MediaBrowser.Providers.Movies { // No biggie. Don't blow up } - catch (DirectoryNotFoundException) + catch (IOException) { // No biggie. Don't blow up } @@ -220,8 +222,7 @@ namespace MediaBrowser.Providers.Movies return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool + Url = url }); } @@ -284,11 +285,12 @@ namespace MediaBrowser.Providers.Movies { Url = url, ResourcePool = FanartArtistProvider.Current.FanArtResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { - using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await response.CopyToAsync(fileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs index 82a2dfbe9..6788bdc9c 100644 --- a/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs +++ b/MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs @@ -13,7 +13,9 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Extensions; namespace MediaBrowser.Providers.Movies @@ -24,16 +26,16 @@ namespace MediaBrowser.Providers.Movies private readonly ILogger _logger; private readonly IJsonSerializer _jsonSerializer; private readonly ILibraryManager _libraryManager; - private readonly IFileSystem _fileSystem; + private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem) + public GenericMovieDbInfo(ILogger logger, IJsonSerializer jsonSerializer, ILibraryManager libraryManager, IFileSystem fileSystem) { _logger = logger; _jsonSerializer = jsonSerializer; _libraryManager = libraryManager; - _fileSystem = fileSystem; + _fileSystem = fileSystem; } public async Task<MetadataResult<T>> GetMetadata(ItemLookupInfo itemId, CancellationToken cancellationToken) @@ -269,6 +271,8 @@ namespace MediaBrowser.Providers.Movies //and the rest from crew if (movieData.casts != null && movieData.casts.crew != null) { + var keepTypes = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer }; + foreach (var person in movieData.casts.crew) { // Normalize this @@ -278,6 +282,12 @@ namespace MediaBrowser.Providers.Movies type = PersonType.Writer; } + if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + var personInfo = new PersonInfo { Name = person.name.Trim(), diff --git a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs index 49f341f26..59b4b5198 100644 --- a/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbImageProvider.cs @@ -14,6 +14,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Movies { @@ -21,11 +22,13 @@ namespace MediaBrowser.Providers.Movies { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; - public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) + public MovieDbImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; + _fileSystem = fileSystem; } public string Name @@ -196,7 +199,7 @@ namespace MediaBrowser.Providers.Movies if (!string.IsNullOrEmpty(path)) { - var fileInfo = new FileInfo(path); + var fileInfo = _fileSystem.GetFileInfo(path); if (fileInfo.Exists) { @@ -217,8 +220,7 @@ namespace MediaBrowser.Providers.Movies return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs index b0d119ff9..8e4b86519 100644 --- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -17,8 +16,11 @@ using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Net; namespace MediaBrowser.Providers.Movies @@ -432,6 +434,7 @@ namespace MediaBrowser.Providers.Movies options.ResourcePool = MovieDbResourcePool; _lastRequestTicks = DateTime.UtcNow.Ticks; + options.BufferContent = true; options.UserAgent = "Emby/" + _appHost.ApplicationVersion; return await _httpClient.Get(options).ConfigureAwait(false); @@ -655,8 +658,7 @@ namespace MediaBrowser.Providers.Movies return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs index 81df7b805..36297914d 100644 --- a/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs +++ b/MediaBrowser.Providers/Movies/MovieDbTrailerProvider.cs @@ -46,8 +46,7 @@ namespace MediaBrowser.Providers.Movies return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Movies/MovieMetadataService.cs b/MediaBrowser.Providers/Movies/MovieMetadataService.cs index 83be9ca6f..2187d6df1 100644 --- a/MediaBrowser.Providers/Movies/MovieMetadataService.cs +++ b/MediaBrowser.Providers/Movies/MovieMetadataService.cs @@ -6,8 +6,10 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Providers.Movies { diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 4f87b2036..e5dbba902 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -9,7 +9,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { diff --git a/MediaBrowser.Providers/Music/ArtistMetadataService.cs b/MediaBrowser.Providers/Music/ArtistMetadataService.cs index b2f975b13..1bd427ac9 100644 --- a/MediaBrowser.Providers/Music/ArtistMetadataService.cs +++ b/MediaBrowser.Providers/Music/ArtistMetadataService.cs @@ -9,7 +9,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs index efaa730b2..c13d19c43 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbAlbumImageProvider.cs @@ -87,8 +87,7 @@ namespace MediaBrowser.Providers.Music return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs index 5963e90e2..3f969b609 100644 --- a/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbAlbumProvider.cs @@ -13,7 +13,9 @@ using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { @@ -162,12 +164,11 @@ namespace MediaBrowser.Providers.Music using (var response = await _httpClient.Get(new HttpRequestOptions { Url = url, - ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool, CancellationToken = cancellationToken }).ConfigureAwait(false)) { - using (var xmlFileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs index 2fcc14c83..6ca1d83d8 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbArtistImageProvider.cs @@ -129,8 +129,7 @@ namespace MediaBrowser.Providers.Music return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = AudioDbArtistProvider.Current.AudioDbResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs index 8bcb01228..0f0c31e6e 100644 --- a/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs +++ b/MediaBrowser.Providers/Music/AudioDbArtistProvider.cs @@ -12,7 +12,9 @@ using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { @@ -150,13 +152,14 @@ namespace MediaBrowser.Providers.Music { Url = url, ResourcePool = AudioDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); - using (var xmlFileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var xmlFileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 67ddd8981..faa8bb3ad 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -7,7 +7,9 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; using System.Linq; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { diff --git a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs index 2f09d64af..d22929664 100644 --- a/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtAlbumProvider.cs @@ -14,7 +14,9 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; namespace MediaBrowser.Providers.Music @@ -92,7 +94,7 @@ namespace MediaBrowser.Providers.Music { } - catch (DirectoryNotFoundException) + catch (IOException) { } @@ -209,8 +211,7 @@ namespace MediaBrowser.Providers.Music return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs index 6afa80507..6fd0d82bd 100644 --- a/MediaBrowser.Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Providers/Music/FanArtArtistProvider.cs @@ -17,7 +17,9 @@ using System.Net; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Net; using MediaBrowser.Model.Serialization; @@ -96,7 +98,7 @@ namespace MediaBrowser.Providers.Music { } - catch (DirectoryNotFoundException) + catch (IOException) { } @@ -204,8 +206,7 @@ namespace MediaBrowser.Providers.Music return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanArtResourcePool + Url = url }); } @@ -255,11 +256,12 @@ namespace MediaBrowser.Providers.Music { Url = url, ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { - using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var saveFileStream = _fileSystem.GetFileStream(jsonPath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await response.CopyToAsync(saveFileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs index 9ed8f0a00..ec31824db 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzAlbumProvider.cs @@ -7,6 +7,7 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Model.Providers; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Net; @@ -15,6 +16,7 @@ using System.Threading; using System.Threading.Tasks; using System.Xml; using MediaBrowser.Model.Serialization; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.Music { @@ -26,15 +28,17 @@ namespace MediaBrowser.Providers.Music private readonly IApplicationHost _appHost; private readonly ILogger _logger; private readonly IJsonSerializer _json; + private readonly IXmlReaderSettingsFactory _xmlSettings; public static string MusicBrainzBaseUrl = "https://www.musicbrainz.org"; - public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger, IJsonSerializer json) + public MusicBrainzAlbumProvider(IHttpClient httpClient, IApplicationHost appHost, ILogger logger, IJsonSerializer json, IXmlReaderSettingsFactory xmlSettings) { _httpClient = httpClient; _appHost = appHost; _logger = logger; _json = json; + _xmlSettings = xmlSettings; Current = this; } @@ -71,35 +75,50 @@ namespace MediaBrowser.Providers.Music if (!string.IsNullOrWhiteSpace(url)) { - var doc = await GetMusicBrainzResponse(url, isNameSearch, cancellationToken).ConfigureAwait(false); - - return GetResultsFromResponse(doc); + using (var stream = await GetMusicBrainzResponse(url, isNameSearch, cancellationToken).ConfigureAwait(false)) + { + return GetResultsFromResponse(stream); + } } return new List<RemoteSearchResult>(); } - private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc) + private List<RemoteSearchResult> GetResultsFromResponse(Stream stream) { - return ReleaseResult.Parse(doc).Select(i => + using (var oReader = new StreamReader(stream, Encoding.UTF8)) { - var result = new RemoteSearchResult - { - Name = i.Title, - ProductionYear = i.Year - }; + var settings = _xmlSettings.Create(false); - if (!string.IsNullOrWhiteSpace(i.ReleaseId)) - { - result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId); - } - if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId)) + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; + + using (var reader = XmlReader.Create(oReader, settings)) { - result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId); - } + var results = ReleaseResult.Parse(reader); - return result; - }); + return results.Select(i => + { + var result = new RemoteSearchResult + { + Name = i.Title, + ProductionYear = i.Year + }; + + if (!string.IsNullOrWhiteSpace(i.ReleaseId)) + { + result.SetProviderId(MetadataProviders.MusicBrainzAlbum, i.ReleaseId); + } + if (!string.IsNullOrWhiteSpace(i.ReleaseGroupId)) + { + result.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, i.ReleaseGroupId); + } + + return result; + }).ToList(); + } + } } public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo id, CancellationToken cancellationToken) @@ -191,9 +210,22 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), artistId); - var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); + using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - return ReleaseResult.Parse(doc, 1).FirstOrDefault(); + using (var reader = XmlReader.Create(oReader, settings)) + { + return ReleaseResult.Parse(reader).FirstOrDefault(); + } + } + } } private async Task<ReleaseResult> GetReleaseResultByArtistName(string albumName, string artistName, CancellationToken cancellationToken) @@ -202,9 +234,22 @@ namespace MediaBrowser.Providers.Music WebUtility.UrlEncode(albumName), WebUtility.UrlEncode(artistName)); - var doc = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); + using (var stream = await GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - return ReleaseResult.Parse(doc, 1).FirstOrDefault(); + using (var reader = XmlReader.Create(oReader, settings)) + { + return ReleaseResult.Parse(reader).FirstOrDefault(); + } + } + } } private class ReleaseResult @@ -215,108 +260,154 @@ namespace MediaBrowser.Providers.Music public string Overview; public int? Year; - public static List<ReleaseResult> Parse(XmlDocument doc, int? limit = null) + public static List<ReleaseResult> Parse(XmlReader reader) { - var docElem = doc.DocumentElement; - var list = new List<ReleaseResult>(); - - if (docElem == null) - { - return list; - } - - var releaseList = docElem.FirstChild; - if (releaseList == null) - { - return list; - } + reader.MoveToContent(); + reader.Read(); - var nodes = releaseList.ChildNodes; - - if (nodes != null) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - foreach (var node in nodes.Cast<XmlNode>()) + if (reader.NodeType == XmlNodeType.Element) { - if (string.Equals(node.Name, "release", StringComparison.OrdinalIgnoreCase)) + switch (reader.Name) { - var releaseId = node.Attributes["id"].Value; - var releaseGroupId = GetReleaseGroupIdFromReleaseNode(node); - - list.Add(new ReleaseResult - { - ReleaseId = releaseId, - ReleaseGroupId = releaseGroupId, - Title = GetValueFromReleaseNode(node, "title"), - Overview = GetValueFromReleaseNode(node, "annotation"), - Year = GetYearFromReleaseNode(node, "date") - }); - - if (limit.HasValue && list.Count >= limit.Value) - { - break; - } + case "release-list": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subReader = reader.ReadSubtree()) + { + return ParseReleaseList(subReader); + } + } + default: + { + reader.Skip(); + break; + } } } + else + { + reader.Read(); + } } - return list; + return new List<ReleaseResult>(); } - private static int? GetYearFromReleaseNode(XmlNode node, string name) + private static List<ReleaseResult> ParseReleaseList(XmlReader reader) { - var subNodes = node.ChildNodes; - if (subNodes != null) + var list = new List<ReleaseResult>(); + + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - foreach (var subNode in subNodes.Cast<XmlNode>()) + if (reader.NodeType == XmlNodeType.Element) { - if (string.Equals(subNode.Name, name, StringComparison.OrdinalIgnoreCase)) + switch (reader.Name) { - DateTime date; - if (DateTime.TryParse(subNode.InnerText, out date)) - { - return date.Year; - } - - return null; + case "release": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + var releaseId = reader.GetAttribute("id"); + + using (var subReader = reader.ReadSubtree()) + { + var release = ParseRelease(subReader, releaseId); + if (release != null) + { + list.Add(release); + } + } + break; + } + default: + { + reader.Skip(); + break; + } } } + else + { + reader.Read(); + } } - return null; + return list; } - private static string GetValueFromReleaseNode(XmlNode node, string name) + private static ReleaseResult ParseRelease(XmlReader reader, string releaseId) { - var subNodes = node.ChildNodes; - if (subNodes != null) + var result = new ReleaseResult { - foreach (var subNode in subNodes.Cast<XmlNode>()) - { - if (string.Equals(subNode.Name, name, StringComparison.OrdinalIgnoreCase)) - { - return subNode.InnerText; - } - } - } + ReleaseId = releaseId + }; - return null; - } + reader.MoveToContent(); + reader.Read(); - private static string GetReleaseGroupIdFromReleaseNode(XmlNode node) - { - var subNodes = node.ChildNodes; - if (subNodes != null) + // http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - foreach (var subNode in subNodes.Cast<XmlNode>()) + if (reader.NodeType == XmlNodeType.Element) { - if (string.Equals(subNode.Name, "release-group", StringComparison.OrdinalIgnoreCase)) + switch (reader.Name) { - return subNode.Attributes["id"].Value; + case "title": + { + result.Title = reader.ReadElementContentAsString(); + break; + } + case "date": + { + var val = reader.ReadElementContentAsString(); + DateTime date; + if (DateTime.TryParse(val, out date)) + { + result.Year = date.Year; + } + break; + } + case "annotation": + { + result.Overview = reader.ReadElementContentAsString(); + break; + } + case "release-group": + { + result.ReleaseGroupId = reader.GetAttribute("id"); + reader.Skip(); + break; + } + default: + { + reader.Skip(); + break; + } } } + else + { + reader.Read(); + } } - return null; + return result; } } @@ -330,33 +421,87 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/release-group/?query=reid:{0}", releaseEntryId); - var doc = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false); + using (var stream = await GetMusicBrainzResponse(url, false, cancellationToken).ConfigureAwait(false)) + { + using (var oReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); - var docElem = doc.DocumentElement; + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - if (docElem == null) - { - return null; - } + using (var reader = XmlReader.Create(oReader, settings)) + { + reader.MoveToContent(); + reader.Read(); - var releaseList = docElem.FirstChild; - if (releaseList == null) - { - return null; + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "release-group-list": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subReader = reader.ReadSubtree()) + { + return GetFirstReleaseGroupId(subReader); + } + } + default: + { + reader.Skip(); + break; + } + } + } + else + { + reader.Read(); + } + } + return null; + } + } } + } - var nodes = releaseList.ChildNodes; + private string GetFirstReleaseGroupId(XmlReader reader) + { + reader.MoveToContent(); + reader.Read(); - if (nodes != null) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - foreach (var node in nodes.Cast<XmlNode>()) + if (reader.NodeType == XmlNodeType.Element) { - if (string.Equals(node.Name, "release-group", StringComparison.OrdinalIgnoreCase)) + switch (reader.Name) { - return node.Attributes["id"].Value; + case "release-group": + { + return reader.GetAttribute("id"); + } + default: + { + reader.Skip(); + break; + } } } + else + { + reader.Read(); + } } + return null; } @@ -402,7 +547,9 @@ namespace MediaBrowser.Providers.Music using (var stream = await _httpClient.Get(options).ConfigureAwait(false)) { - list = _json.DeserializeFromStream<List<MbzUrl>>(stream); + var results = _json.DeserializeFromStream<List<MbzUrl>>(stream); + + list = results; } _lastMbzUrlQueryTicks = DateTime.UtcNow.Ticks; } @@ -432,37 +579,30 @@ namespace MediaBrowser.Providers.Music /// <param name="isSearch">if set to <c>true</c> [is search].</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task{XmlDocument}.</returns> - internal async Task<XmlDocument> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken) + internal async Task<Stream> GetMusicBrainzResponse(string url, bool isSearch, CancellationToken cancellationToken) { var urlInfo = await GetMbzUrl().ConfigureAwait(false); + var throttleMs = urlInfo.throttleMs; - if (urlInfo.throttleMs > 0) + if (throttleMs > 0) { // MusicBrainz is extremely adamant about limiting to one request per second - await Task.Delay(urlInfo.throttleMs, cancellationToken).ConfigureAwait(false); + _logger.Debug("Throttling MusicBrainz by {0}ms", throttleMs.ToString(CultureInfo.InvariantCulture)); + await Task.Delay(throttleMs, cancellationToken).ConfigureAwait(false); } url = urlInfo.url.TrimEnd('/') + url; - var doc = new XmlDocument(); - var options = new HttpRequestOptions { Url = url, CancellationToken = cancellationToken, UserAgent = _appHost.Name + "/" + _appHost.ApplicationVersion, - ResourcePool = _musicBrainzResourcePool + ResourcePool = _musicBrainzResourcePool, + BufferContent = throttleMs > 0 }; - using (var xml = await _httpClient.Get(options).ConfigureAwait(false)) - { - using (var oReader = new StreamReader(xml, Encoding.UTF8)) - { - doc.Load(oReader); - } - } - - return doc; + return await _httpClient.Get(options).ConfigureAwait(false); } public int Order diff --git a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs index 88128bdc7..1a2b13e94 100644 --- a/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs +++ b/MediaBrowser.Providers/Music/MusicBrainzArtistProvider.cs @@ -6,17 +6,27 @@ using MediaBrowser.Model.Providers; using System; using System.Collections.Generic; using System.Globalization; +using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Controller.Extensions; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.Music { public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo> { + private readonly IXmlReaderSettingsFactory _xmlSettings; + + public MusicBrainzArtistProvider(IXmlReaderSettingsFactory xmlSettings) + { + _xmlSettings = xmlSettings; + } + public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken) { var musicBrainzId = searchInfo.GetMusicBrainzArtistId(); @@ -25,10 +35,11 @@ namespace MediaBrowser.Providers.Music { var url = string.Format("/ws/2/artist/?query=arid:{0}", musicBrainzId); - var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken) - .ConfigureAwait(false); - - return GetResultsFromResponse(doc); + using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, false, cancellationToken) + .ConfigureAwait(false)) + { + return GetResultsFromResponse(stream); + } } else { @@ -37,13 +48,14 @@ namespace MediaBrowser.Providers.Music var url = String.Format("/ws/2/artist/?query=artist:\"{0}\"", UrlEncode(nameToSearch)); - var doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); - - var results = GetResultsFromResponse(doc).ToList(); - - if (results.Count > 0) + using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) { - return results; + var results = GetResultsFromResponse(stream).ToList(); + + if (results.Count > 0) + { + return results; + } } if (HasDiacritics(searchInfo.Name)) @@ -51,73 +63,165 @@ namespace MediaBrowser.Providers.Music // Try again using the search with accent characters url url = String.Format("/ws/2/artist/?query=artistaccent:\"{0}\"", UrlEncode(nameToSearch)); - doc = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false); - - return GetResultsFromResponse(doc); + using (var stream = await MusicBrainzAlbumProvider.Current.GetMusicBrainzResponse(url, true, cancellationToken).ConfigureAwait(false)) + { + return GetResultsFromResponse(stream); + } } } return new List<RemoteSearchResult>(); } - private IEnumerable<RemoteSearchResult> GetResultsFromResponse(XmlDocument doc) + private IEnumerable<RemoteSearchResult> GetResultsFromResponse(Stream stream) { - var list = new List<RemoteSearchResult>(); + using (var oReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); - var docElem = doc.DocumentElement; + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - if (docElem == null) - { - return list; - } + using (var reader = XmlReader.Create(oReader, settings)) + { + reader.MoveToContent(); + reader.Read(); - var artistList = docElem.FirstChild; - if (artistList == null) - { - return list; + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "artist-list": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subReader = reader.ReadSubtree()) + { + return ParseArtistList(subReader); + } + } + default: + { + reader.Skip(); + break; + } + } + } + else + { + reader.Read(); + } + } + + return new List<RemoteSearchResult>(); + } } + } - var nodes = artistList.ChildNodes; + private IEnumerable<RemoteSearchResult> ParseArtistList(XmlReader reader) + { + var list = new List<RemoteSearchResult>(); - if (nodes != null) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - foreach (var node in nodes.Cast<XmlNode>()) + if (reader.NodeType == XmlNodeType.Element) { - if (node.Attributes != null) + switch (reader.Name) { - string name = null; - string overview = null; - string mbzId = node.Attributes["id"].Value; - - foreach (var child in node.ChildNodes.Cast<XmlNode>()) - { - if (string.Equals(child.Name, "name", StringComparison.OrdinalIgnoreCase)) + case "artist": { - name = child.InnerText; + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + var mbzId = reader.GetAttribute("id"); + + using (var subReader = reader.ReadSubtree()) + { + var artist = ParseArtist(subReader, mbzId); + if (artist != null) + { + list.Add(artist); + } + } + break; } - if (string.Equals(child.Name, "annotation", StringComparison.OrdinalIgnoreCase)) + default: { - overview = child.InnerText; + reader.Skip(); + break; } - } + } + } + else + { + reader.Read(); + } + } - if (!string.IsNullOrWhiteSpace(mbzId) && !string.IsNullOrWhiteSpace(name)) - { - var result = new RemoteSearchResult - { - Name = name, - Overview = overview - }; + return list; + } - result.SetProviderId(MetadataProviders.MusicBrainzArtist, mbzId); + private RemoteSearchResult ParseArtist(XmlReader reader, string artistId) + { + var result = new RemoteSearchResult(); - list.Add(result); - } + reader.MoveToContent(); + reader.Read(); + + // http://stackoverflow.com/questions/2299632/why-does-xmlreader-skip-every-other-element-if-there-is-no-whitespace-separator + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "name": + { + result.Name = reader.ReadElementContentAsString(); + break; + } + case "annotation": + { + result.Overview = reader.ReadElementContentAsString(); + break; + } + default: + { + // there is sort-name if ever needed + reader.Skip(); + break; + } } } + else + { + reader.Read(); + } } - return list; + result.SetProviderId(MetadataProviders.MusicBrainzArtist, artistId); + + if (string.IsNullOrWhiteSpace(artistId) || string.IsNullOrWhiteSpace(result.Name)) + { + return null; + } + + return result; } public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo id, CancellationToken cancellationToken) @@ -159,21 +263,7 @@ namespace MediaBrowser.Providers.Music /// <returns><c>true</c> if the specified text has diacritics; otherwise, <c>false</c>.</returns> private bool HasDiacritics(string text) { - return !String.Equals(text, RemoveDiacritics(text), StringComparison.Ordinal); - } - - /// <summary> - /// Removes the diacritics. - /// </summary> - /// <param name="text">The text.</param> - /// <returns>System.String.</returns> - 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); } /// <summary> diff --git a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs index a50c07721..f46b6b128 100644 --- a/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs +++ b/MediaBrowser.Providers/Music/MusicVideoMetadataService.cs @@ -7,7 +7,9 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; using System.Linq; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Music { diff --git a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs index d7b96271b..f1ffd09db 100644 --- a/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs +++ b/MediaBrowser.Providers/MusicGenres/MusicGenreMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.MusicGenres { diff --git a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs index 90d172bc2..812f0443e 100644 --- a/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbImageProvider.cs @@ -1,4 +1,4 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -12,6 +12,8 @@ using MediaBrowser.Model.Serialization; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Providers.Omdb { @@ -68,9 +70,7 @@ namespace MediaBrowser.Providers.Omdb return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = OmdbProvider.ResourcePool - + Url = url }); } diff --git a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs index 428bde2f2..db551b763 100644 --- a/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbItemProvider.cs @@ -1,4 +1,4 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -18,6 +18,8 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Providers.Omdb { @@ -129,7 +131,8 @@ namespace MediaBrowser.Providers.Omdb { Url = url, ResourcePool = OmdbProvider.ResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { @@ -294,8 +297,7 @@ namespace MediaBrowser.Providers.Omdb return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = OmdbProvider.ResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Omdb/OmdbProvider.cs index 9c7a16fab..721e31b07 100644 --- a/MediaBrowser.Providers/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Omdb/OmdbProvider.cs @@ -1,4 +1,4 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -14,6 +14,8 @@ using System.Net; using System.Text; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Providers.Omdb { @@ -35,7 +37,7 @@ namespace MediaBrowser.Providers.Omdb } public async Task Fetch<T>(MetadataResult<T> itemResult, string imdbId, string language, string country, CancellationToken cancellationToken) - where T :BaseItem + where T : BaseItem { if (string.IsNullOrWhiteSpace(imdbId)) { @@ -46,25 +48,25 @@ namespace MediaBrowser.Providers.Omdb var result = await GetRootObject(imdbId, cancellationToken); - // Only take the name and rating if the user's language is set to english, since Omdb has no localization - if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) - { - item.Name = result.Title; + // Only take the name and rating if the user's language is set to english, since Omdb has no localization + if (string.Equals(language, "en", StringComparison.OrdinalIgnoreCase)) + { + item.Name = result.Title; - if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase)) - { - item.OfficialRating = result.Rated; - } + if (string.Equals(country, "us", StringComparison.OrdinalIgnoreCase)) + { + item.OfficialRating = result.Rated; } + } - int year; + int year; - if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 - && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out year) - && year >= 0) - { - item.ProductionYear = year; - } + if (!string.IsNullOrEmpty(result.Year) && result.Year.Length >= 4 + && int.TryParse(result.Year.Substring(0, 4), NumberStyles.Number, _usCulture, out year) + && year >= 0) + { + item.ProductionYear = year; + } // Seeing some bogus RT data on omdb for series, so filter it out here // RT doesn't even have tv series @@ -85,33 +87,33 @@ namespace MediaBrowser.Providers.Omdb int voteCount; - if (!string.IsNullOrEmpty(result.imdbVotes) - && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount) - && voteCount >= 0) - { - item.VoteCount = voteCount; - } + if (!string.IsNullOrEmpty(result.imdbVotes) + && int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount) + && voteCount >= 0) + { + item.VoteCount = voteCount; + } - float imdbRating; + float imdbRating; - if (!string.IsNullOrEmpty(result.imdbRating) - && float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating) - && imdbRating >= 0) - { - item.CommunityRating = imdbRating; - } + if (!string.IsNullOrEmpty(result.imdbRating) + && float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating) + && imdbRating >= 0) + { + item.CommunityRating = imdbRating; + } - if (!string.IsNullOrEmpty(result.Website)) - { - item.HomePageUrl = result.Website; - } + if (!string.IsNullOrEmpty(result.Website)) + { + item.HomePageUrl = result.Website; + } - if (!string.IsNullOrWhiteSpace(result.imdbID)) - { - item.SetProviderId(MetadataProviders.Imdb, result.imdbID); - } + if (!string.IsNullOrWhiteSpace(result.imdbID)) + { + item.SetProviderId(MetadataProviders.Imdb, result.imdbID); + } - ParseAdditionalMetadata(itemResult, result); + ParseAdditionalMetadata(itemResult, result); } public async Task<bool> FetchEpisodeData<T>(MetadataResult<T> itemResult, int episodeNumber, int seasonNumber, string imdbId, string language, string country, CancellationToken cancellationToken) @@ -133,7 +135,7 @@ namespace MediaBrowser.Providers.Omdb RootObject result = null; - foreach (var episode in seasonResult.Episodes) + foreach (var episode in (seasonResult.Episodes ?? new RootObject[] { })) { if (episode.Episode == episodeNumber) { @@ -223,7 +225,7 @@ namespace MediaBrowser.Providers.Omdb string resultString; - using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) + using (Stream stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { @@ -242,7 +244,7 @@ namespace MediaBrowser.Providers.Omdb string resultString; - using (Stream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read, 131072)) + using (Stream stream = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read)) { using (var reader = new StreamReader(stream, new UTF8Encoding(false))) { @@ -298,7 +300,8 @@ namespace MediaBrowser.Providers.Omdb { Url = url, ResourcePool = ResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { @@ -338,7 +341,8 @@ namespace MediaBrowser.Providers.Omdb { Url = url, ResourcePool = ResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs index 93eee69ae..372813790 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonImageProvider.cs @@ -135,8 +135,7 @@ namespace MediaBrowser.Providers.People return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs index c954e6323..3645a5f8d 100644 --- a/MediaBrowser.Providers/People/MovieDbPersonProvider.cs +++ b/MediaBrowser.Providers/People/MovieDbPersonProvider.cs @@ -16,7 +16,9 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; @@ -80,7 +82,7 @@ namespace MediaBrowser.Providers.People }; result.SetProviderId(MetadataProviders.Tmdb, info.id.ToString(_usCulture)); - result.SetProviderId(MetadataProviders.Imdb, info.imdb_id.ToString(_usCulture)); + result.SetProviderId(MetadataProviders.Imdb, info.imdb_id); return new[] { result }; } @@ -250,7 +252,7 @@ namespace MediaBrowser.Providers.People { _fileSystem.CreateDirectory(Path.GetDirectoryName(dataFilePath)); - using (var fs = _fileSystem.GetFileStream(dataFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fs = _fileSystem.GetFileStream(dataFilePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await json.CopyToAsync(fs).ConfigureAwait(false); } @@ -406,8 +408,7 @@ namespace MediaBrowser.Providers.People return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/People/PersonMetadataService.cs b/MediaBrowser.Providers/People/PersonMetadataService.cs index 0f8bd8b66..a306212ff 100644 --- a/MediaBrowser.Providers/People/PersonMetadataService.cs +++ b/MediaBrowser.Providers/People/PersonMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.People { diff --git a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs index 2d4d484e6..dd3bfb4f8 100644 --- a/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/People/TvdbPersonImageProvider.cs @@ -15,6 +15,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.People { @@ -23,12 +25,16 @@ namespace MediaBrowser.Providers.People private readonly IServerConfigurationManager _config; private readonly ILibraryManager _libraryManager; private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; + private readonly IXmlReaderSettingsFactory _xmlSettings; - public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IHttpClient httpClient) + public TvdbPersonImageProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IHttpClient httpClient, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) { _config = config; _libraryManager = libraryManager; _httpClient = httpClient; + _fileSystem = fileSystem; + _xmlSettings = xmlSettings; } public string Name @@ -56,13 +62,10 @@ namespace MediaBrowser.Providers.People public Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, CancellationToken cancellationToken) { - // Avoid implicitly captured closure - var itemName = item.Name; - var seriesWithPerson = _libraryManager.GetItemList(new InternalItemsQuery { IncludeItemTypes = new[] { typeof(Series).Name }, - Person = itemName + PersonIds = new[] { item.Id.ToString("N") } }).Cast<Series>() .Where(i => TvdbSeriesProvider.IsValidSeries(i.ProviderIds)) @@ -89,7 +92,7 @@ namespace MediaBrowser.Providers.People { return null; } - catch (DirectoryNotFoundException) + catch (IOException) { return null; } @@ -97,46 +100,57 @@ namespace MediaBrowser.Providers.People private RemoteImageInfo GetImageInfo(string xmlFile, string personName, CancellationToken cancellationToken) { - 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)) { - cancellationToken.ThrowIfCancellationRequested(); + reader.MoveToContent(); + reader.Read(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - switch (reader.Name) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) { - case "Actor": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Actor": { - var info = FetchImageInfoFromActorNode(personName, subtree); - - if (info != null) + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) { - return info; + var info = FetchImageInfoFromActorNode(personName, subtree); + + if (info != null) + { + return info; + } } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } + } + else + { + reader.Read(); } } } @@ -154,12 +168,14 @@ namespace MediaBrowser.Providers.People /// <returns>System.String.</returns> private RemoteImageInfo FetchImageInfoFromActorNode(string personName, XmlReader reader) { - reader.MoveToContent(); - string name = null; string image = null; - while (reader.Read()) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -182,6 +198,10 @@ namespace MediaBrowser.Providers.People break; } } + else + { + reader.Read(); + } } if (!string.IsNullOrEmpty(name) && !string.IsNullOrEmpty(image) && @@ -209,8 +229,7 @@ namespace MediaBrowser.Providers.People return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs index 9bfff6b84..b64b2f24e 100644 --- a/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoAlbumMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Photos { diff --git a/MediaBrowser.Providers/Photos/PhotoHelper.cs b/MediaBrowser.Providers/Photos/PhotoHelper.cs deleted file mode 100644 index 2334c792e..000000000 --- a/MediaBrowser.Providers/Photos/PhotoHelper.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Text; - -namespace MediaBrowser.Providers.Photos -{ - public static class PhotoHelper - { - public static string Dec2Frac(double dbl) - { - char neg = ' '; - double dblDecimal = dbl; - if (dblDecimal == (int)dblDecimal) return dblDecimal.ToString(); //return no if it's not a decimal - if (dblDecimal < 0) - { - dblDecimal = Math.Abs(dblDecimal); - neg = '-'; - } - var whole = (int)Math.Truncate(dblDecimal); - string decpart = dblDecimal.ToString().Replace(Math.Truncate(dblDecimal) + ".", ""); - double rN = Convert.ToDouble(decpart); - double rD = Math.Pow(10, decpart.Length); - - string rd = Recur(decpart); - int rel = Convert.ToInt32(rd); - if (rel != 0) - { - rN = rel; - rD = (int)Math.Pow(10, rd.Length) - 1; - } - //just a few prime factors for testing purposes - var primes = new[] { 47, 43, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 }; - foreach (int i in primes) ReduceNo(i, ref rD, ref rN); - - rN = rN + (whole * rD); - return string.Format("{0}{1}/{2}", neg, rN, rD); - } - - /// <summary> - /// Finds out the recurring decimal in a specified number - /// </summary> - /// <param name="db">Number to check</param> - /// <returns></returns> - private static string Recur(string db) - { - if (db.Length < 13) return "0"; - var sb = new StringBuilder(); - for (int i = 0; i < 7; i++) - { - sb.Append(db[i]); - int dlength = (db.Length / sb.ToString().Length); - int occur = Occurence(sb.ToString(), db); - if (dlength == occur || dlength == occur - sb.ToString().Length) - { - return sb.ToString(); - } - } - return "0"; - } - - /// <summary> - /// Checks for number of occurence of specified no in a number - /// </summary> - /// <param name="s">The no to check occurence times</param> - /// <param name="check">The number where to check this</param> - /// <returns></returns> - private static int Occurence(string s, string check) - { - int i = 0; - int d = s.Length; - string ds = check; - for (int n = (ds.Length / d); n > 0; n--) - { - if (ds.Contains(s)) - { - i++; - ds = ds.Remove(ds.IndexOf(s, System.StringComparison.Ordinal), d); - } - } - return i; - } - - /// <summary> - /// Reduces a fraction given the numerator and denominator - /// </summary> - /// <param name="i">Number to use in an attempt to reduce fraction</param> - /// <param name="rD">the Denominator</param> - /// <param name="rN">the Numerator</param> - private static void ReduceNo(int i, ref double rD, ref double rN) - { - //keep reducing until divisibility ends - while ((rD % i) < 1e-10 && (rN % i) < 1e-10) - { - rN = rN / i; - rD = rD / i; - } - } - } -} diff --git a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs index fb6ff6f09..617a510b8 100644 --- a/MediaBrowser.Providers/Photos/PhotoMetadataService.cs +++ b/MediaBrowser.Providers/Photos/PhotoMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Photos { diff --git a/MediaBrowser.Providers/Photos/PhotoProvider.cs b/MediaBrowser.Providers/Photos/PhotoProvider.cs deleted file mode 100644 index c48c3d09b..000000000 --- a/MediaBrowser.Providers/Photos/PhotoProvider.cs +++ /dev/null @@ -1,169 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using TagLib; -using TagLib.IFD; -using TagLib.IFD.Entries; -using TagLib.IFD.Tags; - -namespace MediaBrowser.Providers.Photos -{ - public class PhotoProvider : ICustomMetadataProvider<Photo>, IHasItemChangeMonitor, IForcedProvider - { - private readonly ILogger _logger; - - public PhotoProvider(ILogger logger) - { - _logger = logger; - } - - public Task<ItemUpdateType> FetchAsync(Photo item, MetadataRefreshOptions options, CancellationToken cancellationToken) - { - item.SetImagePath(ImageType.Primary, item.Path); - - // Examples: https://github.com/mono/taglib-sharp/blob/a5f6949a53d09ce63ee7495580d6802921a21f14/tests/fixtures/TagLib.Tests.Images/NullOrientationTest.cs - - try - { - using (var file = TagLib.File.Create(item.Path)) - { - var image = file as TagLib.Image.File; - - var tag = file.GetTag(TagTypes.TiffIFD) as IFDTag; - - if (tag != null) - { - var structure = tag.Structure; - - if (structure != null) - { - var exif = structure.GetEntry(0, (ushort)IFDEntryTag.ExifIFD) as SubIFDEntry; - - if (exif != null) - { - var exifStructure = exif.Structure; - - if (exifStructure != null) - { - var entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ApertureValue) as RationalIFDEntry; - - if (entry != null) - { - double val = entry.Value.Numerator; - val /= entry.Value.Denominator; - item.Aperture = val; - } - - entry = exifStructure.GetEntry(0, (ushort)ExifEntryTag.ShutterSpeedValue) as RationalIFDEntry; - - if (entry != null) - { - double val = entry.Value.Numerator; - val /= entry.Value.Denominator; - item.ShutterSpeed = val; - } - } - } - } - } - - item.CameraMake = image.ImageTag.Make; - item.CameraModel = image.ImageTag.Model; - - item.Width = image.Properties.PhotoWidth; - item.Height = image.Properties.PhotoHeight; - - var rating = image.ImageTag.Rating; - if (rating.HasValue) - { - item.CommunityRating = rating; - } - else - { - item.CommunityRating = null; - } - - item.Overview = image.ImageTag.Comment; - - if (!string.IsNullOrWhiteSpace(image.ImageTag.Title)) - { - item.Name = image.ImageTag.Title; - } - - var dateTaken = image.ImageTag.DateTime; - if (dateTaken.HasValue) - { - item.DateCreated = dateTaken.Value; - item.PremiereDate = dateTaken.Value; - item.ProductionYear = dateTaken.Value.Year; - } - - item.Genres = image.ImageTag.Genres.ToList(); - item.Tags = image.ImageTag.Keywords.ToList(); - item.Software = image.ImageTag.Software; - - if (image.ImageTag.Orientation == TagLib.Image.ImageOrientation.None) - { - item.Orientation = null; - } - else - { - Model.Drawing.ImageOrientation orientation; - if (Enum.TryParse(image.ImageTag.Orientation.ToString(), true, out orientation)) - { - item.Orientation = orientation; - } - } - - item.ExposureTime = image.ImageTag.ExposureTime; - item.FocalLength = image.ImageTag.FocalLength; - - item.Latitude = image.ImageTag.Latitude; - item.Longitude = image.ImageTag.Longitude; - item.Altitude = image.ImageTag.Altitude; - - if (image.ImageTag.ISOSpeedRatings.HasValue) - { - item.IsoSpeedRating = Convert.ToInt32(image.ImageTag.ISOSpeedRatings.Value); - } - else - { - item.IsoSpeedRating = null; - } - } - } - catch (Exception e) - { - _logger.ErrorException("Image Provider - Error reading image tag for {0}", e, item.Path); - } - - const ItemUpdateType result = ItemUpdateType.ImageUpdate | ItemUpdateType.MetadataImport; - return Task.FromResult(result); - } - - public string Name - { - get { return "Embedded Information"; } - } - - public bool HasChanged(IHasMetadata item, IDirectoryService directoryService) - { - if (item.EnableRefreshOnDateModifiedChange && !string.IsNullOrWhiteSpace(item.Path) && item.LocationType == LocationType.FileSystem) - { - var file = directoryService.GetFile(item.Path); - if (file != null && file.LastWriteTimeUtc != item.DateModified) - { - return true; - } - } - - return false; - } - } -} diff --git a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs index d1d5ba7de..cfdba0fb2 100644 --- a/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs +++ b/MediaBrowser.Providers/Playlists/PlaylistMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Playlists { diff --git a/MediaBrowser.Providers/Studios/StudioMetadataService.cs b/MediaBrowser.Providers/Studios/StudioMetadataService.cs index 9ee594a66..c773b7896 100644 --- a/MediaBrowser.Providers/Studios/StudioMetadataService.cs +++ b/MediaBrowser.Providers/Studios/StudioMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Studios { diff --git a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs index bfb7eb8fd..bf017d148 100644 --- a/MediaBrowser.Providers/Studios/StudiosImageProvider.cs +++ b/MediaBrowser.Providers/Studios/StudiosImageProvider.cs @@ -11,7 +11,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Studios { @@ -88,7 +90,7 @@ namespace MediaBrowser.Providers.Studios private RemoteImageInfo GetImage(IHasImages item, string filename, ImageType type, string remoteFilename) { - var list = ImageUtils.GetAvailableImages(filename); + var list = ImageUtils.GetAvailableImages(filename, _fileSystem); var match = ImageUtils.FindMatch(item, list); diff --git a/MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs b/MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs deleted file mode 100644 index f520915d8..000000000 --- a/MediaBrowser.Providers/Subtitles/ConfigurationExtension.cs +++ /dev/null @@ -1,29 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Model.Providers; -using System.Collections.Generic; - -namespace MediaBrowser.Providers.Subtitles -{ - public static class ConfigurationExtension - { - public static SubtitleOptions GetSubtitleConfiguration(this IConfigurationManager manager) - { - return manager.GetConfiguration<SubtitleOptions>("subtitles"); - } - } - - public class SubtitleConfigurationFactory : IConfigurationFactory - { - public IEnumerable<ConfigurationStore> GetConfigurations() - { - return new List<ConfigurationStore> - { - new ConfigurationStore - { - Key = "subtitles", - ConfigurationType = typeof (SubtitleOptions) - } - }; - } - } -} diff --git a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs b/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs deleted file mode 100644 index 271f8170f..000000000 --- a/MediaBrowser.Providers/Subtitles/OpenSubtitleDownloader.cs +++ /dev/null @@ -1,340 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Controller.Security; -using MediaBrowser.Controller.Subtitles; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Providers; -using MediaBrowser.Model.Serialization; -using OpenSubtitlesHandler; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.Subtitles -{ - public class OpenSubtitleDownloader : ISubtitleProvider, IDisposable - { - private readonly ILogger _logger; - private readonly IHttpClient _httpClient; - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - private readonly IServerConfigurationManager _config; - private readonly IEncryptionManager _encryption; - - private readonly IJsonSerializer _json; - - public OpenSubtitleDownloader(ILogManager logManager, IHttpClient httpClient, IServerConfigurationManager config, IEncryptionManager encryption, IJsonSerializer json) - { - _logger = logManager.GetLogger(GetType().Name); - _httpClient = httpClient; - _config = config; - _encryption = encryption; - _json = json; - - _config.NamedConfigurationUpdating += _config_NamedConfigurationUpdating; - - Utilities.HttpClient = httpClient; - OpenSubtitles.SetUserAgent("mediabrowser.tv"); - } - - private const string PasswordHashPrefix = "h:"; - void _config_NamedConfigurationUpdating(object sender, ConfigurationUpdateEventArgs e) - { - if (!string.Equals(e.Key, "subtitles", StringComparison.OrdinalIgnoreCase)) - { - return; - } - - var options = (SubtitleOptions)e.NewConfiguration; - - if (options != null && - !string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash) && - !options.OpenSubtitlesPasswordHash.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) - { - options.OpenSubtitlesPasswordHash = EncryptPassword(options.OpenSubtitlesPasswordHash); - } - } - - private string EncryptPassword(string password) - { - return PasswordHashPrefix + _encryption.EncryptString(password); - } - - private string DecryptPassword(string password) - { - if (password == null || - !password.StartsWith(PasswordHashPrefix, StringComparison.OrdinalIgnoreCase)) - { - return string.Empty; - } - - return _encryption.DecryptString(password.Substring(2)); - } - - public string Name - { - get { return "Open Subtitles"; } - } - - private SubtitleOptions GetOptions() - { - return _config.GetSubtitleConfiguration(); - } - - public IEnumerable<VideoContentType> SupportedMediaTypes - { - get - { - var options = GetOptions(); - - if (string.IsNullOrWhiteSpace(options.OpenSubtitlesUsername) || - string.IsNullOrWhiteSpace(options.OpenSubtitlesPasswordHash)) - { - return new VideoContentType[] { }; - } - - return new[] { VideoContentType.Episode, VideoContentType.Movie }; - } - } - - public Task<SubtitleResponse> GetSubtitles(string id, CancellationToken cancellationToken) - { - return GetSubtitlesInternal(id, GetOptions(), cancellationToken); - } - - private DateTime _lastRateLimitException; - private async Task<SubtitleResponse> GetSubtitlesInternal(string id, - SubtitleOptions options, - CancellationToken cancellationToken) - { - if (string.IsNullOrWhiteSpace(id)) - { - throw new ArgumentNullException("id"); - } - - var idParts = id.Split(new[] { '-' }, 3); - - var format = idParts[0]; - var language = idParts[1]; - var ossId = idParts[2]; - - var downloadsList = new[] { int.Parse(ossId, _usCulture) }; - - await Login(cancellationToken).ConfigureAwait(false); - - if ((DateTime.UtcNow - _lastRateLimitException).TotalHours < 1) - { - throw new ApplicationException("OpenSubtitles rate limit reached"); - } - - var resultDownLoad = await OpenSubtitles.DownloadSubtitlesAsync(downloadsList, cancellationToken).ConfigureAwait(false); - - if ((resultDownLoad.Status ?? string.Empty).IndexOf("407", StringComparison.OrdinalIgnoreCase) != -1) - { - _lastRateLimitException = DateTime.UtcNow; - throw new ApplicationException("OpenSubtitles rate limit reached"); - } - - if (!(resultDownLoad is MethodResponseSubtitleDownload)) - { - throw new ApplicationException("Invalid response type"); - } - - var results = ((MethodResponseSubtitleDownload)resultDownLoad).Results; - - _lastRateLimitException = DateTime.MinValue; - - if (results.Count == 0) - { - var msg = string.Format("Subtitle with Id {0} was not found. Name: {1}. Status: {2}. Message: {3}", - ossId, - resultDownLoad.Name ?? string.Empty, - resultDownLoad.Status ?? string.Empty, - resultDownLoad.Message ?? string.Empty); - - throw new ResourceNotFoundException(msg); - } - - var data = Convert.FromBase64String(results.First().Data); - - return new SubtitleResponse - { - Format = format, - Language = language, - - Stream = new MemoryStream(Utilities.Decompress(new MemoryStream(data))) - }; - } - - private DateTime _lastLogin; - private async Task Login(CancellationToken cancellationToken) - { - if ((DateTime.UtcNow - _lastLogin).TotalSeconds < 60) - { - return; - } - - var options = GetOptions(); - - var user = options.OpenSubtitlesUsername ?? string.Empty; - var password = DecryptPassword(options.OpenSubtitlesPasswordHash); - - var loginResponse = await OpenSubtitles.LogInAsync(user, password, "en", cancellationToken).ConfigureAwait(false); - - if (!(loginResponse is MethodResponseLogIn)) - { - throw new Exception("Authentication to OpenSubtitles failed."); - } - - _lastLogin = DateTime.UtcNow; - } - - public async Task<IEnumerable<NameIdPair>> GetSupportedLanguages(CancellationToken cancellationToken) - { - await Login(cancellationToken).ConfigureAwait(false); - - var result = OpenSubtitles.GetSubLanguages("en"); - if (!(result is MethodResponseGetSubLanguages)) - { - _logger.Error("Invalid response type"); - return new List<NameIdPair>(); - } - - var results = ((MethodResponseGetSubLanguages)result).Languages; - - return results.Select(i => new NameIdPair - { - Name = i.LanguageName, - Id = i.SubLanguageID - }); - } - - private string NormalizeLanguage(string language) - { - // Problem with Greek subtitle download #1349 - if (string.Equals (language, "gre", StringComparison.OrdinalIgnoreCase)) { - - return "ell"; - } - - return language; - } - - public async Task<IEnumerable<RemoteSubtitleInfo>> Search(SubtitleSearchRequest request, CancellationToken cancellationToken) - { - var imdbIdText = request.GetProviderId(MetadataProviders.Imdb); - long imdbId = 0; - - switch (request.ContentType) - { - case VideoContentType.Episode: - if (!request.IndexNumber.HasValue || !request.ParentIndexNumber.HasValue || string.IsNullOrEmpty(request.SeriesName)) - { - _logger.Debug("Episode information missing"); - return new List<RemoteSubtitleInfo>(); - } - break; - case VideoContentType.Movie: - if (string.IsNullOrEmpty(request.Name)) - { - _logger.Debug("Movie name missing"); - return new List<RemoteSubtitleInfo>(); - } - if (string.IsNullOrWhiteSpace(imdbIdText) || !long.TryParse(imdbIdText.TrimStart('t'), NumberStyles.Any, _usCulture, out imdbId)) - { - _logger.Debug("Imdb id missing"); - return new List<RemoteSubtitleInfo>(); - } - break; - } - - if (string.IsNullOrEmpty(request.MediaPath)) - { - _logger.Debug("Path Missing"); - return new List<RemoteSubtitleInfo>(); - } - - await Login(cancellationToken).ConfigureAwait(false); - - var subLanguageId = NormalizeLanguage(request.Language); - var hash = Utilities.ComputeHash(request.MediaPath); - var fileInfo = new FileInfo(request.MediaPath); - var movieByteSize = fileInfo.Length; - var searchImdbId = request.ContentType == VideoContentType.Movie ? imdbId.ToString(_usCulture) : ""; - var subtitleSearchParameters = request.ContentType == VideoContentType.Episode - ? new List<SubtitleSearchParameters> { - new SubtitleSearchParameters(subLanguageId, - query: request.SeriesName, - season: request.ParentIndexNumber.Value.ToString(_usCulture), - episode: request.IndexNumber.Value.ToString(_usCulture)) - } - : new List<SubtitleSearchParameters> { - new SubtitleSearchParameters(subLanguageId, imdbid: searchImdbId), - new SubtitleSearchParameters(subLanguageId, query: request.Name, imdbid: searchImdbId) - }; - var parms = new List<SubtitleSearchParameters> { - new SubtitleSearchParameters( subLanguageId, - movieHash: hash, - movieByteSize: movieByteSize, - imdbid: searchImdbId ), - }; - parms.AddRange(subtitleSearchParameters); - var result = await OpenSubtitles.SearchSubtitlesAsync(parms.ToArray(), cancellationToken).ConfigureAwait(false); - if (!(result is MethodResponseSubtitleSearch)) - { - _logger.Error("Invalid response type"); - return new List<RemoteSubtitleInfo>(); - } - - Predicate<SubtitleSearchResult> mediaFilter = - x => - request.ContentType == VideoContentType.Episode - ? !string.IsNullOrEmpty(x.SeriesSeason) && !string.IsNullOrEmpty(x.SeriesEpisode) && - int.Parse(x.SeriesSeason, _usCulture) == request.ParentIndexNumber && - int.Parse(x.SeriesEpisode, _usCulture) == request.IndexNumber - : !string.IsNullOrEmpty(x.IDMovieImdb) && long.Parse(x.IDMovieImdb, _usCulture) == imdbId; - - var results = ((MethodResponseSubtitleSearch)result).Results; - - // Avoid implicitly captured closure - var hasCopy = hash; - - return results.Where(x => x.SubBad == "0" && mediaFilter(x) && (!request.IsPerfectMatch || string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase))) - .OrderBy(x => (string.Equals(x.MovieHash, hash, StringComparison.OrdinalIgnoreCase) ? 0 : 1)) - .ThenBy(x => Math.Abs(long.Parse(x.MovieByteSize, _usCulture) - movieByteSize)) - .ThenByDescending(x => int.Parse(x.SubDownloadsCnt, _usCulture)) - .ThenByDescending(x => double.Parse(x.SubRating, _usCulture)) - .Select(i => new RemoteSubtitleInfo - { - Author = i.UserNickName, - Comment = i.SubAuthorComment, - CommunityRating = float.Parse(i.SubRating, _usCulture), - DownloadCount = int.Parse(i.SubDownloadsCnt, _usCulture), - Format = i.SubFormat, - ProviderName = Name, - ThreeLetterISOLanguageName = i.SubLanguageID, - - Id = i.SubFormat + "-" + i.SubLanguageID + "-" + i.IDSubtitleFile, - - Name = i.SubFileName, - DateCreated = DateTime.Parse(i.SubAddDate, _usCulture), - IsHashMatch = i.MovieHash == hasCopy - - }).Where(i => !string.Equals(i.Format, "sub", StringComparison.OrdinalIgnoreCase) && !string.Equals(i.Format, "idx", StringComparison.OrdinalIgnoreCase)); - } - - public void Dispose() - { - _config.NamedConfigurationUpdating -= _config_NamedConfigurationUpdating; - } - } -} diff --git a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs index a57e7d2c0..cd741bed5 100644 --- a/MediaBrowser.Providers/Subtitles/SubtitleManager.cs +++ b/MediaBrowser.Providers/Subtitles/SubtitleManager.cs @@ -16,7 +16,9 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Subtitles { @@ -131,7 +133,7 @@ namespace MediaBrowser.Providers.Subtitles { //var isText = MediaStream.IsTextFormat(response.Format); - using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fs = _fileSystem.GetFileStream(savePath, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await stream.CopyToAsync(fs).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/DummySeasonProvider.cs b/MediaBrowser.Providers/TV/DummySeasonProvider.cs index b74eac219..f9f247999 100644 --- a/MediaBrowser.Providers/TV/DummySeasonProvider.cs +++ b/MediaBrowser.Providers/TV/DummySeasonProvider.cs @@ -1,7 +1,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -9,7 +8,10 @@ using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { diff --git a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs index 90a185ce0..538d96c17 100644 --- a/MediaBrowser.Providers/TV/EpisodeMetadataService.cs +++ b/MediaBrowser.Providers/TV/EpisodeMetadataService.cs @@ -8,7 +8,9 @@ using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -18,40 +20,46 @@ namespace MediaBrowser.Providers.TV { var updateType = await base.BeforeSave(item, isFullRefresh, currentUpdateType).ConfigureAwait(false); - if (updateType <= ItemUpdateType.None) + var seriesName = item.FindSeriesName(); + if (!string.Equals(item.SeriesName, seriesName, StringComparison.Ordinal)) { - if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal)) - { - updateType |= ItemUpdateType.MetadataImport; - } + item.SeriesName = seriesName; + updateType |= ItemUpdateType.MetadataImport; } - if (updateType <= ItemUpdateType.None) + + var seriesSortName = item.FindSeriesSortName(); + if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal)) { - if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal)) - { - updateType |= ItemUpdateType.MetadataImport; - } + item.SeriesSortName = seriesSortName; + updateType |= ItemUpdateType.MetadataImport; } - if (updateType <= ItemUpdateType.None) + + var seasonName = item.FindSeasonName(); + if (!string.Equals(item.SeasonName, seasonName, StringComparison.Ordinal)) { - if (!string.Equals(item.SeasonName, item.FindSeasonName(), StringComparison.Ordinal)) - { - updateType |= ItemUpdateType.MetadataImport; - } + item.SeasonName = seasonName; + updateType |= ItemUpdateType.MetadataImport; } - if (updateType <= ItemUpdateType.None) + + var seriesId = item.FindSeriesId(); + if (item.SeriesId != seriesId) { - if (item.SeriesId != item.FindSeriesId()) - { - updateType |= ItemUpdateType.MetadataImport; - } + item.SeriesId = seriesId; + updateType |= ItemUpdateType.MetadataImport; } - if (updateType <= ItemUpdateType.None) + + var seasonId = item.FindSeasonId(); + if (item.SeasonId != seasonId) + { + item.SeasonId = seasonId; + updateType |= ItemUpdateType.MetadataImport; + } + + var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey(); + if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal)) { - if (item.SeasonId != item.FindSeasonId()) - { - updateType |= ItemUpdateType.MetadataImport; - } + item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey; + updateType |= ItemUpdateType.MetadataImport; } return updateType; diff --git a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs index e6a47d9d2..884fa297f 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanArtSeasonProvider.cs @@ -18,7 +18,9 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -101,7 +103,7 @@ namespace MediaBrowser.Providers.TV { // No biggie. Don't blow up } - catch (DirectoryNotFoundException) + catch (IOException) { // No biggie. Don't blow up } @@ -222,8 +224,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs index 827a3c50f..8db3eaa79 100644 --- a/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/FanArt/FanartSeriesProvider.cs @@ -20,7 +20,9 @@ using System.Net; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -108,7 +110,7 @@ namespace MediaBrowser.Providers.TV { // No biggie. Don't blow up } - catch (DirectoryNotFoundException) + catch (IOException) { // No biggie. Don't blow up } @@ -222,8 +224,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.Current.FanArtResourcePool + Url = url }); } @@ -319,11 +320,12 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = FanartArtistProvider.Current.FanArtResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = true }).ConfigureAwait(false)) { - using (var fileStream = _fileSystem.GetFileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fileStream = _fileSystem.GetFileStream(path, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { await response.CopyToAsync(fileStream).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 9c212e8a0..4992675da 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -1,7 +1,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -14,11 +13,15 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { - class MissingEpisodeProvider + public class MissingEpisodeProvider { private readonly IServerConfigurationManager _config; private readonly ILogger _logger; @@ -27,21 +30,23 @@ namespace MediaBrowser.Providers.TV private readonly IFileSystem _fileSystem; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private static readonly SemaphoreSlim _resourceLock = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim ResourceLock = new SemaphoreSlim(1, 1); public static bool IsRunning = false; + private readonly IXmlReaderSettingsFactory _xmlSettings; - public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem) + public MissingEpisodeProvider(ILogger logger, IServerConfigurationManager config, ILibraryManager libraryManager, ILocalizationManager localization, IFileSystem fileSystem, IXmlReaderSettingsFactory xmlSettings) { _logger = logger; _config = config; _libraryManager = libraryManager; _localization = localization; _fileSystem = fileSystem; + _xmlSettings = xmlSettings; } public async Task Run(List<IGrouping<string, Series>> series, bool addNewItems, CancellationToken cancellationToken) { - await _resourceLock.WaitAsync(cancellationToken).ConfigureAwait(false); + await ResourceLock.WaitAsync(cancellationToken).ConfigureAwait(false); IsRunning = true; foreach (var seriesGroup in series) @@ -54,9 +59,10 @@ namespace MediaBrowser.Providers.TV { break; } - catch (DirectoryNotFoundException) + catch (IOException ex) { //_logger.Warn("Series files missing for series id {0}", seriesGroup.Key); + _logger.ErrorException("Error in missing episode provider for series id {0}", ex, seriesGroup.Key); } catch (Exception ex) { @@ -65,12 +71,15 @@ namespace MediaBrowser.Providers.TV } IsRunning = false; - _resourceLock.Release(); + ResourceLock.Release(); } private async Task Run(IGrouping<string, Series> group, bool addNewItems, CancellationToken cancellationToken) { - var tvdbId = group.Key; + var seriesList = group.ToList(); + var tvdbId = seriesList + .Select(i => i.GetProviderId(MetadataProviders.Tvdb)) + .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i)); // Todo: Support series by imdb id var seriesProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); @@ -78,7 +87,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(); @@ -108,30 +118,33 @@ namespace MediaBrowser.Providers.TV .Where(i => i.Item1 != -1 && i.Item2 != -1) .ToList(); - var hasBadData = HasInvalidContent(group); + var hasBadData = HasInvalidContent(seriesList); + + // Be conservative here to avoid creating missing episodes for ones they already have + var addMissingEpisodes = !hasBadData && seriesList.All(i => _libraryManager.GetLibraryOptions(i).ImportMissingEpisodes); - var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(group, episodeLookup) + var anySeasonsRemoved = await RemoveObsoleteOrMissingSeasons(seriesList, episodeLookup) .ConfigureAwait(false); - var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(group, episodeLookup) + var anyEpisodesRemoved = await RemoveObsoleteOrMissingEpisodes(seriesList, episodeLookup, addMissingEpisodes) .ConfigureAwait(false); var hasNewEpisodes = false; - if (addNewItems && !group.Any(i => !i.IsInternetMetadataEnabled())) + if (addNewItems && seriesList.All(i => i.IsInternetMetadataEnabled())) { var seriesConfig = _config.Configuration.MetadataOptions.FirstOrDefault(i => string.Equals(i.ItemType, typeof(Series).Name, StringComparison.OrdinalIgnoreCase)); if (seriesConfig == null || !seriesConfig.DisabledMetadataFetchers.Contains(TvdbSeriesProvider.Current.Name, StringComparer.OrdinalIgnoreCase)) { - hasNewEpisodes = await AddMissingEpisodes(group.ToList(), hasBadData, seriesDataPath, episodeLookup, cancellationToken) + hasNewEpisodes = await AddMissingEpisodes(seriesList, addMissingEpisodes, seriesDataPath, episodeLookup, cancellationToken) .ConfigureAwait(false); } } if (hasNewEpisodes || anySeasonsRemoved || anyEpisodesRemoved) { - foreach (var series in group) + foreach (var series in seriesList) { var directoryService = new DirectoryService(_logger, _fileSystem); @@ -170,13 +183,9 @@ namespace MediaBrowser.Providers.TV /// Adds the missing episodes. /// </summary> /// <param name="series">The series.</param> - /// <param name="seriesHasBadData">if set to <c>true</c> [series has bad data].</param> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="episodeLookup">The episode lookup.</param> - /// <param name="cancellationToken">The cancellation token.</param> /// <returns>Task.</returns> private async Task<bool> AddMissingEpisodes(List<Series> series, - bool seriesHasBadData, + bool addMissingEpisodes, string seriesDataPath, IEnumerable<Tuple<int, int>> episodeLookup, CancellationToken cancellationToken) @@ -223,15 +232,18 @@ namespace MediaBrowser.Providers.TV { continue; } + var now = DateTime.UtcNow; var targetSeries = DetermineAppropriateSeries(series, tuple.Item1); var seasonOffset = TvdbSeriesProvider.GetSeriesOffset(targetSeries.ProviderIds) ?? ((targetSeries.AnimeSeriesIndex ?? 1) - 1); + var unairedThresholdDays = 1; + now = now.AddDays(0 - unairedThresholdDays); + if (airDate.Value < now) { - // Be conservative here to avoid creating missing episodes for ones they already have - if (!seriesHasBadData) + if (addMissingEpisodes) { // tvdb has a lot of nearly blank episodes _logger.Info("Creating virtual missing episode {0} {1}x{2}", targetSeries.Name, tuple.Item1, tuple.Item2); @@ -268,7 +280,8 @@ namespace MediaBrowser.Providers.TV /// Removes the virtual entry after a corresponding physical version has been added /// </summary> private async Task<bool> RemoveObsoleteOrMissingEpisodes(IEnumerable<Series> series, - IEnumerable<Tuple<int, int>> episodeLookup) + IEnumerable<Tuple<int, int>> episodeLookup, + bool allowMissingEpisodes) { var existingEpisodes = (from s in series let seasonOffset = TvdbSeriesProvider.GetSeriesOffset(s.ProviderIds) ?? ((s.AnimeSeriesIndex ?? 1) - 1) @@ -306,6 +319,11 @@ namespace MediaBrowser.Providers.TV return true; } + if (!allowMissingEpisodes && i.Episode.IsMissingEpisode) + { + return true; + } + return false; } @@ -362,7 +380,7 @@ namespace MediaBrowser.Providers.TV var seasonNumber = i.Season.IndexNumber.Value + i.SeasonOffset; // If there's a physical season with the same number, delete it - if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber)) + if (physicalSeasons.Any(p => p.Season.IndexNumber.HasValue && (p.Season.IndexNumber.Value + p.SeasonOffset) == seasonNumber && string.Equals(p.Season.Series.PresentationUniqueKey, i.Season.Series.PresentationUniqueKey, StringComparison.Ordinal))) { return true; } @@ -492,56 +510,65 @@ namespace MediaBrowser.Providers.TV DateTime? airDate = null; - // It appears the best way to filter out invalid entries is to only include those with valid air dates - 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, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) + // It appears the best way to filter out invalid entries is to only include those with valid air dates + using (var streamReader = new StreamReader(fileStream, Encoding.UTF8)) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) + 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)) { - if (reader.NodeType == XmlNodeType.Element) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - switch (reader.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "EpisodeName": - { - var val = reader.ReadElementContentAsString(); - if (string.IsNullOrWhiteSpace(val)) + switch (reader.Name) + { + case "EpisodeName": { - // Not valid, ignore these - return null; - } - break; - } - case "FirstAired": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - DateTime date; - if (DateTime.TryParse(val, out date)) + var val = reader.ReadElementContentAsString(); + if (string.IsNullOrWhiteSpace(val)) { - airDate = date.ToUniversalTime(); + // Not valid, ignore these + return null; } + break; } + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); - break; - } + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out date)) + { + airDate = date.ToUniversalTime(); + } + } - default: - reader.Skip(); - break; + break; + } + default: + { + reader.Skip(); + break; + } + } + } + else + { + reader.Read(); } } } diff --git a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs index 78bce241f..21e327a8f 100644 --- a/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/Omdb/OmdbEpisodeProvider.cs @@ -1,4 +1,4 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; @@ -12,6 +12,8 @@ using MediaBrowser.Providers.Omdb; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; namespace MediaBrowser.Providers.TV { @@ -55,7 +57,7 @@ namespace MediaBrowser.Providers.TV if (OmdbProvider.IsValidSeries(info.SeriesProviderIds) && info.IndexNumber.HasValue && info.ParentIndexNumber.HasValue) { - var seriesImdbId = info.SeriesProviderIds[MetadataProviders.Imdb.ToString()]; + var seriesImdbId = info.GetProviderId(MetadataProviders.Imdb); result.HasMetadata = await new OmdbProvider(_jsonSerializer, _httpClient, _fileSystem, _configurationManager).FetchEpisodeData(result, info.IndexNumber.Value, info.ParentIndexNumber.Value, seriesImdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); } diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs index cf04a1418..af7dea59e 100644 --- a/MediaBrowser.Providers/TV/SeasonMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -9,7 +9,9 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -35,16 +37,31 @@ namespace MediaBrowser.Providers.TV updateType |= SaveIsVirtualItem(item, episodes); } - if (!string.Equals(item.SeriesName, item.FindSeriesName(), StringComparison.Ordinal)) + var seriesName = item.FindSeriesName(); + if (!string.Equals(item.SeriesName, seriesName, StringComparison.Ordinal)) { + item.SeriesName = seriesName; updateType |= ItemUpdateType.MetadataImport; } - if (!string.Equals(item.SeriesSortName, item.FindSeriesSortName(), StringComparison.Ordinal)) + + var seriesSortName = item.FindSeriesSortName(); + if (!string.Equals(item.SeriesSortName, seriesSortName, StringComparison.Ordinal)) + { + item.SeriesSortName = seriesSortName; + updateType |= ItemUpdateType.MetadataImport; + } + + var seriesPresentationUniqueKey = item.FindSeriesPresentationUniqueKey(); + if (!string.Equals(item.SeriesPresentationUniqueKey, seriesPresentationUniqueKey, StringComparison.Ordinal)) { + item.SeriesPresentationUniqueKey = seriesPresentationUniqueKey; updateType |= ItemUpdateType.MetadataImport; } - if (item.SeriesId != item.FindSeriesId()) + + var seriesId = item.FindSeriesId(); + if (item.SeriesId != seriesId) { + item.SeriesId = seriesId; updateType |= ItemUpdateType.MetadataImport; } diff --git a/MediaBrowser.Providers/TV/SeriesMetadataService.cs b/MediaBrowser.Providers/TV/SeriesMetadataService.cs index f440baf5b..0d89e307f 100644 --- a/MediaBrowser.Providers/TV/SeriesMetadataService.cs +++ b/MediaBrowser.Providers/TV/SeriesMetadataService.cs @@ -1,7 +1,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -10,7 +9,10 @@ using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs deleted file mode 100644 index e038a3d28..000000000 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ /dev/null @@ -1,232 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using CommonIO; -using MediaBrowser.Common.ScheduledTasks; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Plugins; - -namespace MediaBrowser.Providers.TV -{ - class SeriesGroup : List<Series>, IGrouping<string, Series> - { - public string Key { get; set; } - } - - class SeriesPostScanTask : ILibraryPostScanTask, IHasOrder - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - private readonly ILocalizationManager _localization; - private readonly IFileSystem _fileSystem; - - public SeriesPostScanTask(ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, ILocalizationManager localization, IFileSystem fileSystem) - { - _libraryManager = libraryManager; - _logger = logger; - _config = config; - _localization = localization; - _fileSystem = fileSystem; - } - - public Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - return RunInternal(progress, cancellationToken); - } - - private Task RunInternal(IProgress<double> progress, CancellationToken cancellationToken) - { - var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true, - GroupByPresentationUniqueKey = false - - }).Cast<Series>().ToList(); - - var seriesGroups = FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); - - return new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem).Run(seriesGroups, true, cancellationToken); - } - - internal static IEnumerable<IGrouping<string, Series>> FindSeriesGroups(List<Series> seriesList) - { - var links = seriesList.ToDictionary(s => s, s => seriesList.Where(c => c != s && ShareProviderId(s, c)).ToList()); - - var visited = new HashSet<Series>(); - - foreach (var series in seriesList) - { - if (!visited.Contains(series)) - { - var group = new SeriesGroup(); - FindAllLinked(series, visited, links, group); - - group.Key = group.Select(s => s.GetProviderId(MetadataProviders.Tvdb)).FirstOrDefault(id => !string.IsNullOrEmpty(id)); - - yield return group; - } - } - } - - private static void FindAllLinked(Series series, HashSet<Series> visited, IDictionary<Series, List<Series>> linksMap, List<Series> results) - { - results.Add(series); - visited.Add(series); - - var links = linksMap[series]; - - foreach (var s in links) - { - if (!visited.Contains(s)) - { - FindAllLinked(s, visited, linksMap, results); - } - } - } - - private static bool ShareProviderId(Series a, Series b) - { - return a.ProviderIds.Any(id => - { - string value; - return b.ProviderIds.TryGetValue(id.Key, out value) && id.Value == value; - }); - } - - public int Order - { - get - { - // Run after tvdb update task - return 1; - } - } - } - - public class CleanMissingEpisodesEntryPoint : IServerEntryPoint - { - private readonly ILibraryManager _libraryManager; - private readonly IServerConfigurationManager _config; - private readonly ILogger _logger; - private readonly ILocalizationManager _localization; - private readonly IFileSystem _fileSystem; - private readonly object _libraryChangedSyncLock = new object(); - private const int LibraryUpdateDuration = 180000; - private readonly ITaskManager _taskManager; - - public CleanMissingEpisodesEntryPoint(ILibraryManager libraryManager, IServerConfigurationManager config, ILogger logger, ILocalizationManager localization, IFileSystem fileSystem, ITaskManager taskManager) - { - _libraryManager = libraryManager; - _config = config; - _logger = logger; - _localization = localization; - _fileSystem = fileSystem; - _taskManager = taskManager; - } - - private Timer LibraryUpdateTimer { get; set; } - - public void Run() - { - _libraryManager.ItemAdded += _libraryManager_ItemAdded; - } - - private void _libraryManager_ItemAdded(object sender, ItemChangeEventArgs e) - { - if (!FilterItem(e.Item)) - { - return; - } - - lock (_libraryChangedSyncLock) - { - if (LibraryUpdateTimer == null) - { - LibraryUpdateTimer = new Timer(LibraryUpdateTimerCallback, null, LibraryUpdateDuration, Timeout.Infinite); - } - else - { - LibraryUpdateTimer.Change(LibraryUpdateDuration, Timeout.Infinite); - } - } - } - - private async void LibraryUpdateTimerCallback(object state) - { - try - { - if (MissingEpisodeProvider.IsRunning) - { - return; - } - - if (_libraryManager.IsScanRunning) - { - return; - } - - var seriesList = _libraryManager.GetItemList(new InternalItemsQuery() - { - IncludeItemTypes = new[] { typeof(Series).Name }, - Recursive = true, - GroupByPresentationUniqueKey = false - - }).Cast<Series>().ToList(); - - var seriesGroups = SeriesPostScanTask.FindSeriesGroups(seriesList).Where(g => !string.IsNullOrEmpty(g.Key)).ToList(); - - await new MissingEpisodeProvider(_logger, _config, _libraryManager, _localization, _fileSystem) - .Run(seriesGroups, false, CancellationToken.None).ConfigureAwait(false); - } - catch (Exception ex) - { - _logger.ErrorException("Error in SeriesPostScanTask", ex); - } - } - - private bool FilterItem(BaseItem item) - { - return item is Episode && item.LocationType != LocationType.Virtual; - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - Dispose(true); - } - - /// <summary> - /// Releases unmanaged and - optionally - managed resources. - /// </summary> - /// <param name="dispose"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param> - protected virtual void Dispose(bool dispose) - { - if (dispose) - { - if (LibraryUpdateTimer != null) - { - LibraryUpdateTimer.Dispose(); - LibraryUpdateTimer = null; - } - - _libraryManager.ItemAdded -= _libraryManager_ItemAdded; - } - } - } -} diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs index 0feb92e89..952c4f934 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeImageProvider.cs @@ -1,8 +1,7 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; @@ -15,6 +14,9 @@ using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs index 748124c03..fcd753264 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbEpisodeProvider.cs @@ -1,9 +1,9 @@ -using CommonIO; +using System; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -16,6 +16,9 @@ using System.Linq; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { @@ -164,9 +167,24 @@ namespace MediaBrowser.Providers.TV //and the rest from crew if (credits.crew != null) { + var keepTypes = new[] { PersonType.Director, PersonType.Writer, PersonType.Producer }; + foreach (var person in credits.crew) { - result.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = person.department }); + // Normalize this + var type = person.department; + if (string.Equals(type, "writing", StringComparison.OrdinalIgnoreCase)) + { + type = PersonType.Writer; + } + + if (!keepTypes.Contains(type ?? string.Empty, StringComparer.OrdinalIgnoreCase) && + !keepTypes.Contains(person.job ?? string.Empty, StringComparer.OrdinalIgnoreCase)) + { + continue; + } + + result.AddPerson(new PersonInfo { Name = person.name.Trim(), Role = person.job, Type = type }); } } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs index 821c26e4b..38831feb6 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbProviderBase.cs @@ -1,7 +1,6 @@ -using CommonIO; +using MediaBrowser.Model.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Localization; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Serialization; using MediaBrowser.Providers.Movies; @@ -11,6 +10,9 @@ using System.Globalization; using System.IO; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs index 194af5b99..1f0cc9e52 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeasonProvider.cs @@ -1,7 +1,6 @@ using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -16,7 +15,10 @@ using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { @@ -119,8 +121,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs index ad46db677..c746e0488 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesImageProvider.cs @@ -13,6 +13,7 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -20,11 +21,13 @@ namespace MediaBrowser.Providers.TV { private readonly IJsonSerializer _jsonSerializer; private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; - public MovieDbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient) + public MovieDbSeriesImageProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, IFileSystem fileSystem) { _jsonSerializer = jsonSerializer; _httpClient = httpClient; + _fileSystem = fileSystem; } public string Name @@ -166,7 +169,7 @@ namespace MediaBrowser.Providers.TV if (!string.IsNullOrEmpty(path)) { - var fileInfo = new FileInfo(path); + var fileInfo = _fileSystem.GetFileInfo(path); if (fileInfo.Exists) { @@ -191,8 +194,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs index fb0678029..5b4ae9745 100644 --- a/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheMovieDb/MovieDbSeriesProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Localization; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; @@ -18,7 +17,10 @@ using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Globalization; namespace MediaBrowser.Providers.TV { @@ -654,8 +656,7 @@ namespace MediaBrowser.Providers.TV return _httpClient.GetResponse(new HttpRequestOptions { CancellationToken = cancellationToken, - Url = url, - ResourcePool = MovieDbProvider.Current.MovieDbResourcePool + Url = url }); } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs index a0280cf40..791f56beb 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeImageProvider.cs @@ -12,7 +12,9 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.TV { @@ -80,67 +82,73 @@ namespace MediaBrowser.Providers.TV // Use XmlReader for best performance using (reader) { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "thumb_width": - { - 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)) - { - width = rval; - } - } - break; - } - - case "thumb_height": - { - 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)) - { - height = rval; - } - } - break; - } - - case "filename": - { - var val = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(val)) - { - url = TVUtils.BannerUrl + val; - } - break; - } - - default: - reader.Skip(); - break; - } - } - } + 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 rval; + + // int.TryParse is local aware, so it can be probamatic, force us culture + if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval)) + { + width = rval; + } + } + break; + } + + case "thumb_height": + { + 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)) + { + 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)) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs index 41a2282d8..99136cd85 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbEpisodeProvider.cs @@ -16,7 +16,10 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -33,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; } @@ -47,11 +52,11 @@ namespace MediaBrowser.Providers.TV { var list = new List<RemoteSearchResult>(); - // 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)) { @@ -59,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 { @@ -91,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. } @@ -110,19 +116,20 @@ namespace MediaBrowser.Providers.TV var result = new MetadataResult<Episode>(); 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 { @@ -132,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. } @@ -153,20 +160,20 @@ namespace MediaBrowser.Providers.TV /// <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 (DirectoryNotFoundException) - { - return new List<XmlReader> (); - } - catch (FileNotFoundException) - { - return new List<XmlReader> (); - } + 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>(); + } } private class EpisodeNumbers @@ -186,59 +193,62 @@ namespace MediaBrowser.Providers.TV /// <returns>Task{System.Boolean}.</returns> private MetadataResult<Episode> FetchEpisodeData(EpisodeInfo id, EpisodeNumbers searchNumbers, string seriesDataPath, CancellationToken cancellationToken) { - var result = new MetadataResult<Episode>() - { - Item = new Episode - { - IndexNumber = id.IndexNumber, - ParentIndexNumber = id.ParentIndexNumber, - IndexNumberEnd = id.IndexNumberEnd - } - }; + var result = new MetadataResult<Episode>() + { + 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<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) - { - var list = new List<XmlReader> (); + private List<XmlReader> GetXmlNodes(string xmlFile, EpisodeInfo searchInfo) + { + var list = new List<XmlReader>(); - 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<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) - { - var files = new List<FileSystemMetadata>(); + private List<FileSystemMetadata> GetEpisodeXmlFiles(int? seasonNumber, int? episodeNumber, int? endingEpisodeNumber, string seriesDataPath) + { + var files = new List<FileSystemMetadata>(); - if (episodeNumber == null) - { - return files; - } + if (episodeNumber == null) + { + return files; + } - var usingAbsoluteData = false; + var usingAbsoluteData = false; if (seasonNumber.HasValue) { @@ -261,230 +271,292 @@ 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)); - } - - var fileInfo = _fileSystem.GetFileInfo(file); - if (fileInfo.Exists) - { - files.Add(fileInfo); - } - else - { - break; - } - - episodeNumber++; - } - - return files; - } - - private XmlReader GetXmlReader(FileSystemMetadata xmlFile) - { - return GetXmlReader (_fileSystem.ReadAllText(xmlFile.FullName, Encoding.UTF8)); - } - - 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 - }); - } - - private List<XmlReader> GetXmlNodesByPremiereDate(string xmlFile, DateTime premiereDate) - { - var list = new List<XmlReader> (); - - 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(); - - // Loop through each element - while (reader.Read()) - { - 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; - } - } - } - } - } - - return list; - } - - 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 (); - - // 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; - } + 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; + } + + episodeNumber++; + } + + return files; + } + + private XmlReader GetXmlReader(FileSystemMetadata xmlFile) + { + return GetXmlReader(_fileSystem.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)) + { + DateTime date; + if (DateTime.TryParse(val, out 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, CancellationToken cancellationToken) + private void FetchMainEpisodeInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); - - result.ResetPeople(); - - // 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(); + // 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 (!string.IsNullOrWhiteSpace(val)) + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "id": { - float num; + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) + { + item.SetProviderId(MetadataProviders.Tvdb, val); + } + break; + } - if (float.TryParse(val, NumberStyles.Any, _usCulture, out num)) + case "IMDB_ID": + { + var val = reader.ReadElementContentAsString(); + if (!string.IsNullOrWhiteSpace(val)) { - item.DvdSeasonNumber = Convert.ToInt32(num); + item.SetProviderId(MetadataProviders.Imdb, val); } + break; } - break; - } + case "DVD_episodenumber": + { + var val = reader.ReadElementContentAsString(); - case "EpisodeNumber": - { - if (!item.IndexNumber.HasValue) + 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": + { + var val = reader.ReadElementContentAsString(); + + if (!item.IndexNumber.HasValue) + { + 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": + { + var val = reader.ReadElementContentAsString(); + + if (!item.ParentIndexNumber.HasValue) + { + 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(); @@ -495,17 +567,32 @@ 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 "airsbefore_episode": + { + var val = reader.ReadElementContentAsString(); - case "SeasonNumber": - { - if (!item.ParentIndexNumber.HasValue) + 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(); @@ -516,225 +603,174 @@ 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.AirsAfterSeasonNumber = rval; } } + + break; } - 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": + { + 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 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(); - 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; - } - } - } - } + 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(); + } + } + } } private void AddPeople<T>(MetadataResult<T> result, string val, string personType) @@ -778,99 +814,104 @@ namespace MediaBrowser.Providers.TV } } - private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken) + private void FetchAdditionalPartInfo(MetadataResult<Episode> result, XmlReader reader, CancellationToken cancellationToken) { var item = result.Item; - // Use XmlReader for best performance - using (reader) - { - reader.MoveToContent(); - - // 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; - } - - 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); - } - } - - 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; - } - } - } - } + // 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) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index d9e1037d8..8488c5669 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -14,8 +14,11 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Model.IO; using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -48,6 +51,7 @@ namespace MediaBrowser.Providers.TV private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; private readonly ILibraryManager _libraryManager; + private readonly IXmlReaderSettingsFactory _xmlSettings; /// <summary> /// Initializes a new instance of the <see cref="TvdbPrescanTask"/> class. @@ -55,13 +59,14 @@ namespace MediaBrowser.Providers.TV /// <param name="logger">The logger.</param> /// <param name="httpClient">The HTTP client.</param> /// <param name="config">The config.</param> - public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem, ILibraryManager libraryManager) + public TvdbPrescanTask(ILogger logger, IHttpClient httpClient, IServerConfigurationManager config, IFileSystem fileSystem, ILibraryManager libraryManager, IXmlReaderSettingsFactory xmlSettings) { _logger = logger; _httpClient = httpClient; _config = config; _fileSystem = fileSystem; _libraryManager = libraryManager; + _xmlSettings = xmlSettings; } protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); @@ -101,7 +106,7 @@ namespace MediaBrowser.Providers.TV string newUpdateTime; - var existingDirectories = Directory.EnumerateDirectories(path) + var existingDirectories = _fileSystem.GetDirectoryPaths(path) .Select(Path.GetFileName) .ToList(); @@ -178,13 +183,11 @@ namespace MediaBrowser.Providers.TV /// <returns>System.String.</returns> private string GetUpdateTime(Stream response) { - 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; using (var streamReader = new StreamReader(response, Encoding.UTF8)) { @@ -192,9 +195,10 @@ namespace MediaBrowser.Providers.TV using (var reader = XmlReader.Create(streamReader, settings)) { reader.MoveToContent(); + reader.Read(); // Loop through each element - while (reader.Read()) + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -209,6 +213,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } } } @@ -251,13 +259,11 @@ namespace MediaBrowser.Providers.TV string updateTime = null; var idList = new List<string>(); - 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; using (var streamReader = new StreamReader(stream, Encoding.UTF8)) { @@ -265,9 +271,10 @@ namespace MediaBrowser.Providers.TV using (var reader = XmlReader.Create(streamReader, settings)) { reader.MoveToContent(); + reader.Read(); // Loop through each element - while (reader.Read()) + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -289,6 +296,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } } } diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs index 4b619665c..e189c292c 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeasonImageProvider.cs @@ -16,7 +16,10 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -27,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 @@ -78,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 } @@ -103,45 +108,56 @@ namespace MediaBrowser.Providers.TV return seasonNumber; } - internal static IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken) + internal static IEnumerable<RemoteImageInfo> 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<RemoteImageInfo>(); - 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(); + reader.Read(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - 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); + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + AddImage(subtree, list, seasonNumber); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } + } + else + { + reader.Read(); } } } @@ -189,7 +205,11 @@ namespace MediaBrowser.Providers.TV int? voteCount = null; string thumbnailUrl = null; - while (reader.Read()) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -290,6 +310,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } if (!string.IsNullOrEmpty(url) && bannerSeason.HasValue && bannerSeason.Value == seasonNumber) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs index c8efbfb14..2595ad585 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesImageProvider.cs @@ -16,7 +16,10 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -26,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 @@ -73,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); } @@ -81,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 } @@ -92,43 +97,54 @@ namespace MediaBrowser.Providers.TV private IEnumerable<RemoteImageInfo> 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<RemoteImageInfo>(); - 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(); + reader.Read(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - 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); + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + AddImage(subtree, list); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } + } + else + { + reader.Read(); } } } @@ -175,7 +191,11 @@ namespace MediaBrowser.Providers.TV int? voteCount = null; string thumbnailUrl = null; - while (reader.Read()) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -277,6 +297,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index 2572a4f58..cc8a90fe3 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -20,8 +20,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using System.Xml; -using CommonIO; -using MediaBrowser.Common.IO; +using MediaBrowser.Model.Globalization; +using MediaBrowser.Model.Xml; namespace MediaBrowser.Providers.TV { @@ -35,13 +35,15 @@ namespace MediaBrowser.Providers.TV private readonly IZipClient _zipClient; private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; + private readonly IXmlReaderSettingsFactory _xmlSettings; private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; - private readonly IMemoryStreamProvider _memoryStreamProvider; + private readonly IMemoryStreamFactory _memoryStreamProvider; + 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, IMemoryStreamFactory memoryStreamProvider, IXmlReaderSettingsFactory xmlSettings, ILocalizationManager localizationManager) { _zipClient = zipClient; _httpClient = httpClient; @@ -50,6 +52,8 @@ namespace MediaBrowser.Providers.TV _logger = logger; _libraryManager = libraryManager; _memoryStreamProvider = memoryStreamProvider; + _xmlSettings = xmlSettings; + _localizationManager = localizationManager; Current = this; } @@ -251,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); @@ -280,22 +285,95 @@ namespace MediaBrowser.Providers.TV }).ConfigureAwait(false)) { - var doc = new XmlDocument(); - doc.Load(result); + return FindSeriesId(result); + } + } + + private string FindSeriesId(Stream stream) + { + using (var streamReader = new StreamReader(stream, Encoding.UTF8)) + { + var settings = _xmlSettings.Create(false); - if (doc.HasChildNodes) + 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(); + reader.Read(); - if (node != null) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - var idResult = node.InnerText; + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Series": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + return FindSeriesId(subtree); + } + } + + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } + } + } + } - _logger.Info("Tvdb GetSeriesByRemoteId produced id of {0}", idResult ?? string.Empty); + return null; + } - return idResult; + private string FindSeriesId(XmlReader reader) + { + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + 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; } } + else + { + reader.Read(); + } } return null; @@ -304,7 +382,7 @@ namespace MediaBrowser.Providers.TV internal static bool IsValidSeries(Dictionary<string, string> seriesProviderIds) { string id; - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out id)) { // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. if (!string.IsNullOrWhiteSpace(id)) @@ -313,7 +391,7 @@ namespace MediaBrowser.Providers.TV } } - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id) && !string.IsNullOrEmpty(id)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out id)) { // This check should ideally never be necessary but we're seeing some cases of this and haven't tracked them down yet. if (!string.IsNullOrWhiteSpace(id)) @@ -332,7 +410,7 @@ namespace MediaBrowser.Providers.TV try { string seriesId; - if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Tvdb.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId)) { var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); @@ -346,7 +424,7 @@ namespace MediaBrowser.Providers.TV return seriesDataPath; } - if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrEmpty(seriesId)) + if (seriesProviderIds.TryGetValue(MetadataProviders.Imdb.ToString(), out seriesId) && !string.IsNullOrWhiteSpace(seriesId)) { var seriesDataPath = GetSeriesDataPath(_config.ApplicationPaths, seriesProviderIds); @@ -401,11 +479,11 @@ namespace MediaBrowser.Providers.TV } return true; } - catch (DirectoryNotFoundException) + catch (FileNotFoundException) { return false; } - catch (FileNotFoundException) + catch (IOException) { return false; } @@ -449,9 +527,11 @@ namespace MediaBrowser.Providers.TV private async Task<IEnumerable<RemoteSearchResult>> FindSeriesInternal(string name, string language, CancellationToken cancellationToken) { var url = string.Format(SeriesSearchUrl, WebUtility.UrlEncode(name), NormalizeLanguage(language)); - var doc = new XmlDocument(); + var searchResults = new List<RemoteSearchResult>(); - using (var results = await _httpClient.Get(new HttpRequestOptions + var comparableName = GetComparableName(name); + + using (var stream = await _httpClient.Get(new HttpRequestOptions { Url = url, ResourcePool = TvDbResourcePool, @@ -459,100 +539,186 @@ namespace MediaBrowser.Providers.TV }).ConfigureAwait(false)) { - doc.Load(results); - } + var settings = _xmlSettings.Create(false); - var searchResults = new List<RemoteSearchResult>(); + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - if (doc.HasChildNodes) - { - var nodes = doc.SelectNodes("//Series"); - var comparableName = GetComparableName(name); - if (nodes != null) + using (var streamReader = new StreamReader(stream, Encoding.UTF8)) { - foreach (XmlNode node in nodes) + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, settings)) { - var searchResult = new RemoteSearchResult + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - SearchProviderName = Name - }; + cancellationToken.ThrowIfCancellationRequested(); - var titles = new List<string>(); + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "Series": + { + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + var searchResult = GetSeriesSearchResultFromSubTree(subtree, comparableName); + if (searchResult != null) + { + searchResult.SearchProviderName = Name; + searchResults.Add(searchResult); + } + } + break; + } - var nameNode = node.SelectSingleNode("./SeriesName"); - if (nameNode != null) - { - titles.Add(GetComparableName(nameNode.InnerText)); + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); + } } + } + } + } - var aliasNode = node.SelectSingleNode("./AliasNames"); - if (aliasNode != null) - { - var alias = aliasNode.InnerText.Split('|').Select(GetComparableName); - titles.AddRange(alias); - } + if (searchResults.Count == 0) + { + _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org."); + } - var imdbIdNode = node.SelectSingleNode("./IMDB_ID"); - if (imdbIdNode != null) - { - var val = imdbIdNode.InnerText; - if (!string.IsNullOrWhiteSpace(val)) + return searchResults; + } + + private RemoteSearchResult GetSeriesSearchResultFromSubTree(XmlReader reader, string comparableName) + { + var searchResult = new RemoteSearchResult + { + SearchProviderName = Name + }; + + var titles = new List<string>(); + string seriesId = null; + + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) + { + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "SeriesName": { - searchResult.SetProviderId(MetadataProviders.Imdb, val); + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + titles.Add(GetComparableName(val)); + } + break; } - } - var bannerNode = node.SelectSingleNode("./banner"); - if (bannerNode != null) - { - var val = bannerNode.InnerText; - if (!string.IsNullOrWhiteSpace(val)) + case "AliasNames": { - searchResult.ImageUrl = TVUtils.BannerUrl + val; + var val = reader.ReadElementContentAsString(); + + var alias = (val ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries).Select(GetComparableName); + titles.AddRange(alias); + break; } - } - var airDateNode = node.SelectSingleNode("./FirstAired"); - if (airDateNode != null) - { - var val = airDateNode.InnerText; - if (!string.IsNullOrWhiteSpace(val)) + case "IMDB_ID": { - DateTime date; - if (DateTime.TryParse(val, out date)) + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) { - searchResult.ProductionYear = date.Year; + searchResult.SetProviderId(MetadataProviders.Imdb, val); } + break; } - } - foreach (var title in titles) - { - if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase)) + case "banner": { - var id = node.SelectSingleNode("./seriesid") ?? - node.SelectSingleNode("./id"); + var val = reader.ReadElementContentAsString(); - if (id != null) + if (!string.IsNullOrWhiteSpace(val)) { - searchResult.Name = title; - searchResult.SetProviderId(MetadataProviders.Tvdb, id.InnerText); - searchResults.Add(searchResult); + searchResult.ImageUrl = TVUtils.BannerUrl + val; } break; } - _logger.Info("TVDb Provider - " + title + " did not match " + comparableName); - } + + case "FirstAired": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + DateTime date; + if (DateTime.TryParse(val, out date)) + { + searchResult.ProductionYear = date.Year; + } + } + break; + } + + case "id": + case "seriesid": + { + var val = reader.ReadElementContentAsString(); + + if (!string.IsNullOrWhiteSpace(val)) + { + seriesId = val; + } + break; + } + + default: + reader.Skip(); + break; } } + else + { + reader.Read(); + } } - if (searchResults.Count == 0) + foreach (var title in titles) { - _logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org."); + if (string.Equals(title, comparableName, StringComparison.OrdinalIgnoreCase)) + { + if (!string.IsNullOrWhiteSpace(seriesId)) + { + searchResult.Name = title; + searchResult.SetProviderId(MetadataProviders.Tvdb, seriesId); + return searchResult; + } + break; + } + _logger.Info("TVDb Provider - " + title + " did not match " + comparableName); } - return searchResults; + return null; } /// <summary> @@ -569,10 +735,10 @@ namespace MediaBrowser.Providers.TV /// </summary> /// <param name="name">The name.</param> /// <returns>System.String.</returns> - 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) { @@ -614,58 +780,74 @@ namespace MediaBrowser.Providers.TV private void FetchSeriesInfo(MetadataResult<Series> 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<DateTime>(); - 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(); + reader.Read(); - if (reader.NodeType == XmlNodeType.Element) + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - 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); + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + FetchDataFromSeriesNode(result, subtree, cancellationToken); + } + break; } - break; - } - case "Episode": - { - using (var subtree = reader.ReadSubtree()) + case "Episode": { - var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); - - if (date.HasValue) + if (reader.IsEmptyElement) { - episiodeAirDates.Add(date.Value); + reader.Read(); + continue; } + using (var subtree = reader.ReadSubtree()) + { + var date = GetFirstAiredDateFromEpisodeNode(subtree, cancellationToken); + + if (date.HasValue) + { + episiodeAirDates.Add(date.Value); + } + } + break; } - break; - } - default: - reader.Skip(); - break; + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); } } } @@ -684,9 +866,10 @@ namespace MediaBrowser.Providers.TV int? seasonNumber = null; reader.MoveToContent(); + reader.Read(); // Loop through each element - while (reader.Read()) + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { cancellationToken.ThrowIfCancellationRequested(); @@ -733,6 +916,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } if (seasonNumber.HasValue && seasonNumber.Value != 0) @@ -750,39 +937,50 @@ namespace MediaBrowser.Providers.TV /// <param name="actorsXmlPath">The actors XML path.</param> private void FetchActors(MetadataResult<Series> result, string actorsXmlPath) { - 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; - using (var streamReader = new StreamReader(actorsXmlPath, Encoding.UTF8)) + 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(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - switch (reader.Name) + if (reader.NodeType == XmlNodeType.Element) { - case "Actor": - { - using (var subtree = reader.ReadSubtree()) + switch (reader.Name) + { + case "Actor": { - FetchDataFromActorNode(result, subtree); + if (reader.IsEmptyElement) + { + reader.Read(); + continue; + } + using (var subtree = reader.ReadSubtree()) + { + FetchDataFromActorNode(result, subtree); + } + break; } + default: + reader.Skip(); break; - } - default: - reader.Skip(); - break; + } + } + else + { + reader.Read(); } } } @@ -801,7 +999,11 @@ namespace MediaBrowser.Providers.TV var personInfo = new PersonInfo(); - while (reader.Read()) + reader.MoveToContent(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -821,6 +1023,7 @@ namespace MediaBrowser.Providers.TV case "id": { + reader.Skip(); break; } @@ -856,6 +1059,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } personInfo.Type = PersonType.Actor; @@ -871,9 +1078,10 @@ namespace MediaBrowser.Providers.TV Series item = result.Item; reader.MoveToContent(); + reader.Read(); // Loop through each element - while (reader.Read()) + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { cancellationToken.ThrowIfCancellationRequested(); @@ -1099,6 +1307,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } } @@ -1111,39 +1323,45 @@ namespace MediaBrowser.Providers.TV /// <returns>Task.</returns> 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); + + settings.CheckCharacters = false; + settings.IgnoreProcessingInstructions = true; + settings.IgnoreComments = true; - using (var streamReader = new StreamReader(xmlFile, Encoding.UTF8)) + 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(); + reader.Read(); + + // Loop through each element + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { - 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); - break; - } + await SaveEpsiodeXml(seriesDataPath, outerXml, lastTvDbUpdateTime).ConfigureAwait(false); + break; + } - default: - reader.Skip(); - break; + default: + reader.Skip(); + break; + } + } + else + { + reader.Read(); } } } @@ -1153,13 +1371,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; @@ -1172,9 +1388,10 @@ namespace MediaBrowser.Providers.TV using (var reader = XmlReader.Create(streamReader, settings)) { reader.MoveToContent(); + reader.Read(); // Loop through each element - while (reader.Read()) + while (!reader.EOF && reader.ReadState == ReadState.Interactive) { if (reader.NodeType == XmlNodeType.Element) { @@ -1233,6 +1450,10 @@ namespace MediaBrowser.Providers.TV break; } } + else + { + reader.Read(); + } } } } @@ -1252,13 +1473,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 - { - Encoding = Encoding.UTF8, - Async = true - })) + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, 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); + } } } @@ -1269,13 +1493,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 - { - Encoding = Encoding.UTF8, - Async = true - })) + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.None, 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); + } } } } @@ -1338,7 +1565,7 @@ namespace MediaBrowser.Providers.TV _fileSystem.DeleteFile(file); } } - catch (DirectoryNotFoundException) + catch (IOException) { // No biggie } @@ -1353,7 +1580,7 @@ namespace MediaBrowser.Providers.TV { string validXml; - using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, true)) + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) { using (var reader = new StreamReader(fileStream)) { @@ -1363,7 +1590,7 @@ namespace MediaBrowser.Providers.TV } } - using (var fileStream = _fileSystem.GetFileStream(file, FileMode.Create, FileAccess.Write, FileShare.Read, true)) + using (var fileStream = _fileSystem.GetFileStream(file, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true)) { using (var writer = new StreamWriter(fileStream)) { diff --git a/MediaBrowser.Providers/Users/UserMetadataService.cs b/MediaBrowser.Providers/Users/UserMetadataService.cs index 0637e9a02..274d04efd 100644 --- a/MediaBrowser.Providers/Users/UserMetadataService.cs +++ b/MediaBrowser.Providers/Users/UserMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Users { diff --git a/MediaBrowser.Providers/Videos/VideoMetadataService.cs b/MediaBrowser.Providers/Videos/VideoMetadataService.cs index a4fc462ef..49f7e11ba 100644 --- a/MediaBrowser.Providers/Videos/VideoMetadataService.cs +++ b/MediaBrowser.Providers/Videos/VideoMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Videos { diff --git a/MediaBrowser.Providers/Years/YearMetadataService.cs b/MediaBrowser.Providers/Years/YearMetadataService.cs index fd65c379d..72f8eb471 100644 --- a/MediaBrowser.Providers/Years/YearMetadataService.cs +++ b/MediaBrowser.Providers/Years/YearMetadataService.cs @@ -6,7 +6,9 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Providers.Manager; using System.Collections.Generic; -using CommonIO; +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.IO; +using MediaBrowser.Model.IO; namespace MediaBrowser.Providers.Years { diff --git a/MediaBrowser.Providers/packages.config b/MediaBrowser.Providers/packages.config deleted file mode 100644 index da365b0ce..000000000 --- a/MediaBrowser.Providers/packages.config +++ /dev/null @@ -1,8 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<packages> - <package id="CommonIO" version="1.0.0.9" targetFramework="net45" /> - <package id="MediaBrowser.BdInfo" version="1.0.0.10" targetFramework="net45" /> - <package id="morelinq" version="1.4.0" targetFramework="net45" /> - <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" /> - <package id="taglib" version="2.1.0.0" targetFramework="net45" /> -</packages>
\ No newline at end of file diff --git a/MediaBrowser.Providers/project.json b/MediaBrowser.Providers/project.json new file mode 100644 index 000000000..fbbe9eaf3 --- /dev/null +++ b/MediaBrowser.Providers/project.json @@ -0,0 +1,17 @@ +{ + "frameworks":{ + "netstandard1.6":{ + "dependencies":{ + "NETStandard.Library":"1.6.0", + } + }, + ".NETPortable,Version=v4.5,Profile=Profile7":{ + "buildOptions": { + "define": [ ] + }, + "frameworkAssemblies":{ + + } + } + } +}
\ No newline at end of file |
