diff options
Diffstat (limited to 'MediaBrowser.Controller')
46 files changed, 2 insertions, 10806 deletions
diff --git a/MediaBrowser.Controller/Extensions/XmlExtensions.cs b/MediaBrowser.Controller/Extensions/XmlExtensions.cs deleted file mode 100644 index ce689e47d..000000000 --- a/MediaBrowser.Controller/Extensions/XmlExtensions.cs +++ /dev/null @@ -1,238 +0,0 @@ -using System; -using System.Globalization; -using System.Xml; - -namespace MediaBrowser.Controller.Extensions -{ - /// <summary> - /// Class XmlExtensions - /// </summary> - public static class XmlExtensions - { - - /// <summary> - /// Safes the get int32. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <returns>System.Int32.</returns> - public static int SafeGetInt32(this XmlDocument doc, string path) - { - return SafeGetInt32(doc, path, 0); - } - - /// <summary> - /// Safes the get int32. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <param name="defaultInt">The default int.</param> - /// <returns>System.Int32.</returns> - public static int SafeGetInt32(this XmlDocument doc, string path, int defaultInt) - { - XmlNode rvalNode = doc.SelectSingleNode(path); - if (rvalNode != null && rvalNode.InnerText.Length > 0) - { - int rval; - if (Int32.TryParse(rvalNode.InnerText, out rval)) - { - return rval; - } - - } - return defaultInt; - } - - /// <summary> - /// The _us culture - /// </summary> - private static readonly CultureInfo _usCulture = new CultureInfo("en-US"); - - /// <summary> - /// Safes the get single. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <param name="minValue">The min value.</param> - /// <param name="maxValue">The max value.</param> - /// <returns>System.Single.</returns> - public static float SafeGetSingle(this XmlDocument doc, string path, float minValue, float maxValue) - { - XmlNode rvalNode = doc.SelectSingleNode(path); - if (rvalNode != null && rvalNode.InnerText.Length > 0) - { - float rval; - // float.TryParse is local aware, so it can be probamatic, force us culture - if (float.TryParse(rvalNode.InnerText, NumberStyles.AllowDecimalPoint, _usCulture, out rval)) - { - if (rval >= minValue && rval <= maxValue) - { - return rval; - } - } - - } - return minValue; - } - - - /// <summary> - /// Safes the get string. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <returns>System.String.</returns> - public static string SafeGetString(this XmlDocument doc, string path) - { - return SafeGetString(doc, path, null); - } - - /// <summary> - /// Safes the get string. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <param name="defaultString">The default string.</param> - /// <returns>System.String.</returns> - public static string SafeGetString(this XmlDocument doc, string path, string defaultString) - { - var rvalNode = doc.SelectSingleNode(path); - - if (rvalNode != null) - { - var text = rvalNode.InnerText; - - return !string.IsNullOrWhiteSpace(text) ? text : defaultString; - } - - return defaultString; - } - - /// <summary> - /// Safes the get DateTime. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <returns>System.DateTime.</returns> - public static DateTime? SafeGetDateTime(this XmlDocument doc, string path) - { - return SafeGetDateTime(doc, path, null); - } - - /// <summary> - /// Safes the get DateTime. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <param name="defaultDate">The default date.</param> - /// <returns>System.DateTime.</returns> - public static DateTime? SafeGetDateTime(this XmlDocument doc, string path, DateTime? defaultDate) - { - var rvalNode = doc.SelectSingleNode(path); - - if (rvalNode != null) - { - var text = rvalNode.InnerText; - DateTime date; - if (DateTime.TryParse(text, out date)) - return date.ToUniversalTime(); - } - return defaultDate; - } - - /// <summary> - /// Safes the get string. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <returns>System.String.</returns> - public static string SafeGetString(this XmlNode doc, string path) - { - return SafeGetString(doc, path, null); - } - - /// <summary> - /// Safes the get string. - /// </summary> - /// <param name="doc">The doc.</param> - /// <param name="path">The path.</param> - /// <param name="defaultValue">The default value.</param> - /// <returns>System.String.</returns> - public static string SafeGetString(this XmlNode doc, string path, string defaultValue) - { - var rvalNode = doc.SelectSingleNode(path); - if (rvalNode != null) - { - var text = rvalNode.InnerText; - - return !string.IsNullOrWhiteSpace(text) ? text : defaultValue; - } - return defaultValue; - } - - /// <summary> - /// Reads the string safe. - /// </summary> - /// <param name="reader">The reader.</param> - /// <returns>System.String.</returns> - public static string ReadStringSafe(this XmlReader reader) - { - var val = reader.ReadElementContentAsString(); - - return string.IsNullOrWhiteSpace(val) ? null : val; - } - - /// <summary> - /// Reads the value safe. - /// </summary> - /// <param name="reader">The reader.</param> - /// <returns>System.String.</returns> - public static string ReadValueSafe(this XmlReader reader) - { - reader.Read(); - - var val = reader.Value; - - return string.IsNullOrWhiteSpace(val) ? null : val; - } - - /// <summary> - /// Reads a float from the current element of an XmlReader - /// </summary> - /// <param name="reader">The reader.</param> - /// <returns>System.Single.</returns> - public static float ReadFloatSafe(this XmlReader reader) - { - string valueString = reader.ReadElementContentAsString(); - - float value = 0; - - if (!string.IsNullOrWhiteSpace(valueString)) - { - // float.TryParse is local aware, so it can be probamatic, force us culture - float.TryParse(valueString, NumberStyles.AllowDecimalPoint, _usCulture, out value); - } - - return value; - } - - /// <summary> - /// Reads an int from the current element of an XmlReader - /// </summary> - /// <param name="reader">The reader.</param> - /// <returns>System.Int32.</returns> - public static int ReadIntSafe(this XmlReader reader) - { - string valueString = reader.ReadElementContentAsString(); - - int value = 0; - - if (!string.IsNullOrWhiteSpace(valueString)) - { - int.TryParse(valueString, out value); - } - - return value; - } - } -}
\ No newline at end of file diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index feb3d90dd..4422f0036 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -78,14 +78,6 @@ <Compile Include="Library\ILibraryPrescanTask.cs" /> <Compile Include="Library\IMetadataSaver.cs" /> <Compile Include="Localization\ILocalizationManager.cs" /> - <Compile Include="Providers\Movies\FanArtMovieUpdatesPrescanTask.cs" /> - <Compile Include="Providers\Movies\MovieDbImagesProvider.cs" /> - <Compile Include="Providers\Music\ArtistsPostScanTask.cs" /> - <Compile Include="Providers\Music\FanArtUpdatesPrescanTask.cs" /> - <Compile Include="Providers\TV\FanArtSeasonProvider.cs" /> - <Compile Include="Providers\TV\FanArtTvUpdatesPrescanTask.cs" /> - <Compile Include="Providers\TV\TvdbPrescanTask.cs" /> - <Compile Include="Providers\TV\TvdbSeriesImageProvider.cs" /> <Compile Include="Session\ISessionManager.cs" /> <Compile Include="Drawing\ImageExtensions.cs" /> <Compile Include="Drawing\ImageHeader.cs" /> @@ -122,7 +114,6 @@ <Compile Include="Entities\Video.cs" /> <Compile Include="Entities\CollectionFolder.cs" /> <Compile Include="Entities\Year.cs" /> - <Compile Include="Extensions\XmlExtensions.cs" /> <Compile Include="IO\FileSystem.cs" /> <Compile Include="IO\IDirectoryWatchers.cs" /> <Compile Include="IO\NativeMethods.cs" /> @@ -131,17 +122,8 @@ <Compile Include="Dto\DtoBuilder.cs" /> <Compile Include="Library\SearchHintInfo.cs" /> <Compile Include="Providers\IProviderManager.cs" /> - <Compile Include="Providers\MediaInfo\MediaEncoderHelpers.cs" /> + <Compile Include="MediaInfo\MediaEncoderHelpers.cs" /> <Compile Include="Providers\MetadataProviderPriority.cs" /> - <Compile Include="Providers\Movies\OpenMovieDatabaseProvider.cs" /> - <Compile Include="Providers\Music\FanArtArtistByNameProvider.cs" /> - <Compile Include="Providers\Music\LastfmAlbumProvider.cs" /> - <Compile Include="Providers\Music\FanArtAlbumProvider.cs" /> - <Compile Include="Providers\Music\FanArtArtistProvider.cs" /> - <Compile Include="Providers\Music\LastfmArtistByNameProvider.cs" /> - <Compile Include="Providers\Music\LastfmArtistProvider.cs" /> - <Compile Include="Providers\Music\LastfmHelper.cs" /> - <Compile Include="Providers\Music\MusicArtistProviderFromJson.cs" /> <Compile Include="Resolvers\BaseItemResolver.cs" /> <Compile Include="Resolvers\BaseVideoResolver.cs" /> <Compile Include="Resolvers\IItemResolver.cs" /> @@ -166,33 +148,8 @@ <Compile Include="Library\IIntroProvider.cs" /> <Compile Include="Plugins\IPluginConfigurationPage.cs" /> <Compile Include="Plugins\IServerEntryPoint.cs" /> - <Compile Include="Providers\Music\LastfmBaseProvider.cs" /> - <Compile Include="Providers\FanartBaseProvider.cs" /> <Compile Include="Providers\IImageEnhancer.cs" /> - <Compile Include="Providers\ImagesByNameProvider.cs" /> - <Compile Include="Providers\MediaInfo\BaseFFMpegProvider.cs" /> - <Compile Include="Providers\MediaInfo\AudioImageProvider.cs" /> - <Compile Include="Providers\MediaInfo\BaseFFProbeProvider.cs" /> <Compile Include="Providers\BaseProviderInfo.cs" /> - <Compile Include="Providers\Movies\FanArtMovieProvider.cs" /> - <Compile Include="Providers\Movies\MovieDbProvider.cs" /> - <Compile Include="Providers\Movies\MovieProviderFromJson.cs" /> - <Compile Include="Providers\Movies\MovieProviderFromXml.cs" /> - <Compile Include="Providers\Movies\PersonProviderFromJson.cs" /> - <Compile Include="Providers\Movies\TmdbPersonProvider.cs" /> - <Compile Include="Providers\TV\EpisodeImageFromMediaLocationProvider.cs" /> - <Compile Include="Providers\TV\EpisodeProviderFromXml.cs" /> - <Compile Include="Providers\TV\EpisodeXmlParser.cs" /> - <Compile Include="Providers\TV\FanArtTVProvider.cs" /> - <Compile Include="Providers\TV\RemoteEpisodeProvider.cs"> - <SubType>Code</SubType> - </Compile> - <Compile Include="Providers\TV\RemoteSeasonProvider.cs"> - <SubType>Code</SubType> - </Compile> - <Compile Include="Providers\TV\RemoteSeriesProvider.cs" /> - <Compile Include="Providers\TV\SeriesProviderFromXml.cs" /> - <Compile Include="Providers\TV\SeriesXmlParser.cs" /> <Compile Include="Resolvers\IResolverIgnoreRule.cs" /> <Compile Include="Resolvers\EntityResolutionHelper.cs" /> <Compile Include="Resolvers\ResolverPriority.cs" /> @@ -202,10 +159,6 @@ <Compile Include="Kernel.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Providers\BaseMetadataProvider.cs" /> - <Compile Include="Providers\MediaInfo\FFProbeAudioInfoProvider.cs" /> - <Compile Include="Providers\FolderProviderFromXml.cs" /> - <Compile Include="Providers\ImageFromMediaLocationProvider.cs" /> - <Compile Include="Providers\MediaInfo\FFProbeVideoInfoProvider.cs" /> <Compile Include="Session\SessionInfo.cs" /> <Compile Include="Sorting\IBaseItemComparer.cs" /> <Compile Include="Sorting\IUserBaseItemComparer.cs" /> diff --git a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs index 91359cd29..4b992fd81 100644 --- a/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs +++ b/MediaBrowser.Controller/MediaInfo/FFMpegManager.cs @@ -2,7 +2,6 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers.MediaInfo; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; diff --git a/MediaBrowser.Controller/Providers/MediaInfo/MediaEncoderHelpers.cs b/MediaBrowser.Controller/MediaInfo/MediaEncoderHelpers.cs index 7ea2a339a..e20a425aa 100644 --- a/MediaBrowser.Controller/Providers/MediaInfo/MediaEncoderHelpers.cs +++ b/MediaBrowser.Controller/MediaInfo/MediaEncoderHelpers.cs @@ -3,7 +3,7 @@ using MediaBrowser.Common.MediaInfo; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Entities; -namespace MediaBrowser.Controller.Providers.MediaInfo +namespace MediaBrowser.Controller.MediaInfo { /// <summary> /// Class MediaEncoderHelpers diff --git a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs b/MediaBrowser.Controller/Providers/FanartBaseProvider.cs deleted file mode 100644 index f5dff25f6..000000000 --- a/MediaBrowser.Controller/Providers/FanartBaseProvider.cs +++ /dev/null @@ -1,106 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Model.Logging; -using System.Collections.Generic; -using System.Threading; - -namespace MediaBrowser.Controller.Providers -{ - /// <summary> - /// Class FanartBaseProvider - /// </summary> - public abstract class FanartBaseProvider : BaseMetadataProvider - { - internal static readonly SemaphoreSlim FanArtResourcePool = new SemaphoreSlim(3, 3); - - /// <summary> - /// The LOG o_ FILE - /// </summary> - protected const string LogoFile = "logo.png"; - - /// <summary> - /// The AR t_ FILE - /// </summary> - protected const string ArtFile = "clearart.png"; - - /// <summary> - /// The THUM b_ FILE - /// </summary> - protected const string ThumbFile = "thumb.jpg"; - - /// <summary> - /// The DIS c_ FILE - /// </summary> - protected const string DiscFile = "disc.png"; - - /// <summary> - /// The BANNE r_ FILE - /// </summary> - protected const string BannerFile = "banner.png"; - - /// <summary> - /// The Backdrop - /// </summary> - protected const string BackdropFile = "backdrop.jpg"; - - /// <summary> - /// The Primary image - /// </summary> - protected const string PrimaryFile = "folder.jpg"; - - /// <summary> - /// The API key - /// </summary> - internal const string ApiKey = "5c6b04c68e904cfed1e6cbc9a9e683d4"; - - protected FanartBaseProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get { return true; } - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Third; } - } - - #region Result Objects - - protected class FanArtImageInfo - { - public string id { get; set; } - public string url { get; set; } - public string likes { get; set; } - } - - protected class FanArtMusicInfo - { - public string mbid_id { get; set; } - public List<FanArtImageInfo> musiclogo { get; set; } - public List<FanArtImageInfo> artistbackground { get; set; } - public List<FanArtImageInfo> artistthumb { get; set; } - public List<FanArtImageInfo> hdmusiclogo { get; set; } - public List<FanArtImageInfo> musicbanner { get; set; } - } - - protected class FanArtMusicResult - { - public FanArtMusicInfo result { get; set; } - } - - #endregion - - } - -} diff --git a/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs b/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs deleted file mode 100644 index 641a291ff..000000000 --- a/MediaBrowser.Controller/Providers/FolderProviderFromXml.cs +++ /dev/null @@ -1,98 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers -{ - /// <summary> - /// Provides metadata for Folders and all subclasses by parsing folder.xml - /// </summary> - public class FolderProviderFromXml : BaseMetadataProvider - { - public FolderProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Folder && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.MetaLocation != null ? item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "folder.xml")) : null; - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - return Fetch(item, cancellationToken); - } - - /// <summary> - /// Fetches the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "folder.xml")); - - if (metadataFile != null) - { - var path = metadataFile.FullName; - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - new BaseItemXmlParser<Folder>(Logger).Fetch((Folder)item, path, cancellationToken); - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - return false; - } - } -} diff --git a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs b/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs deleted file mode 100644 index 1bd4ce0ca..000000000 --- a/MediaBrowser.Controller/Providers/ImageFromMediaLocationProvider.cs +++ /dev/null @@ -1,323 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - /// <summary> - /// Provides images for all types by looking for standard images - folder, backdrop, logo, etc. - /// </summary> - public class ImageFromMediaLocationProvider : BaseMetadataProvider - { - public ImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item.LocationType == LocationType.FileSystem && item.ResolveArgs.IsDirectory; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - // Make sure current image paths still exist - ValidateImages(item); - - cancellationToken.ThrowIfCancellationRequested(); - - // Make sure current backdrop paths still exist - ValidateBackdrops(item); - - cancellationToken.ThrowIfCancellationRequested(); - - PopulateBaseItemImages(item); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - - /// <summary> - /// Validates that images within the item are still on the file system - /// </summary> - /// <param name="item">The item.</param> - private void ValidateImages(BaseItem item) - { - if (item.Images == null) - { - return; - } - - // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below - var deletedKeys = item.Images.ToList().Where(image => - { - var path = image.Value; - - return IsInMetaLocation(item, path) && item.ResolveArgs.GetMetaFileByPath(path) == null; - - }).Select(i => i.Key).ToList(); - - // Now remove them from the dictionary - foreach (var key in deletedKeys) - { - item.Images.Remove(key); - } - } - - /// <summary> - /// Validates that backdrops within the item are still on the file system - /// </summary> - /// <param name="item">The item.</param> - private void ValidateBackdrops(BaseItem item) - { - if (item.BackdropImagePaths == null) - { - return; - } - - // Only validate paths from the same directory - need to copy to a list because we are going to potentially modify the collection below - var deletedImages = item.BackdropImagePaths.Where(path => IsInMetaLocation(item, path) && item.ResolveArgs.GetMetaFileByPath(path) == null).ToList(); - - // Now remove them from the dictionary - foreach (var path in deletedImages) - { - item.BackdropImagePaths.Remove(path); - } - } - - /// <summary> - /// Determines whether [is in same directory] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="path">The path.</param> - /// <returns><c>true</c> if [is in same directory] [the specified item]; otherwise, <c>false</c>.</returns> - private bool IsInMetaLocation(BaseItem item, string path) - { - return string.Equals(Path.GetDirectoryName(path), item.MetaLocation, StringComparison.OrdinalIgnoreCase); - } - - /// <summary> - /// Gets the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="filenameWithoutExtension">The filename without extension.</param> - /// <returns>FileSystemInfo.</returns> - protected virtual FileSystemInfo GetImage(BaseItem item, string filenameWithoutExtension) - { - return BaseItem.SupportedImageExtensions.Select(i => item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.ResolveArgs.Path, filenameWithoutExtension + i))).FirstOrDefault(i => i != null); - } - - /// <summary> - /// Fills in image paths based on files win the folder - /// </summary> - /// <param name="item">The item.</param> - private void PopulateBaseItemImages(BaseItem item) - { - // Primary Image - var image = GetImage(item, "folder") ?? - GetImage(item, "poster") ?? - GetImage(item, "cover") ?? - GetImage(item, "default"); - - if (image != null) - { - item.SetImage(ImageType.Primary, image.FullName); - } - - // Logo Image - image = GetImage(item, "logo"); - - if (image != null) - { - item.SetImage(ImageType.Logo, image.FullName); - } - - // Banner Image - image = GetImage(item, "banner"); - - if (image != null) - { - item.SetImage(ImageType.Banner, image.FullName); - } - - // Clearart - image = GetImage(item, "clearart"); - - if (image != null) - { - item.SetImage(ImageType.Art, image.FullName); - } - - // Thumbnail Image - image = GetImage(item, "thumb"); - - if (image != null) - { - item.SetImage(ImageType.Thumb, image.FullName); - } - - // Box Image - image = GetImage(item, "box"); - - if (image != null) - { - item.SetImage(ImageType.Box, image.FullName); - } - - // BoxRear Image - image = GetImage(item, "boxrear"); - - if (image != null) - { - item.SetImage(ImageType.BoxRear, image.FullName); - } - - // Thumbnail Image - image = GetImage(item, "menu"); - - if (image != null) - { - item.SetImage(ImageType.Menu, image.FullName); - } - - // Backdrop Image - PopulateBackdrops(item); - - // Screenshot Image - image = GetImage(item, "screenshot"); - - var screenshotFiles = new List<string>(); - - if (image != null) - { - screenshotFiles.Add(image.FullName); - } - - var unfound = 0; - for (var i = 1; i <= 20; i++) - { - // Screenshot Image - image = GetImage(item, "screenshot" + i); - - if (image != null) - { - screenshotFiles.Add(image.FullName); - } - else - { - unfound++; - - if (unfound >= 3) - { - break; - } - } - } - - if (screenshotFiles.Count > 0) - { - item.ScreenshotImagePaths = screenshotFiles; - } - } - - /// <summary> - /// Populates the backdrops. - /// </summary> - /// <param name="item">The item.</param> - private void PopulateBackdrops(BaseItem item) - { - var backdropFiles = new List<string>(); - - PopulateBackdrops(item, backdropFiles, "backdrop", "backdrop"); - - // Support plex/xbmc conventions - PopulateBackdrops(item, backdropFiles, "fanart", "fanart-"); - PopulateBackdrops(item, backdropFiles, "background", "background-"); - - if (backdropFiles.Count > 0) - { - item.BackdropImagePaths = backdropFiles; - } - } - - /// <summary> - /// Populates the backdrops. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="backdropFiles">The backdrop files.</param> - /// <param name="filename">The filename.</param> - /// <param name="numberedSuffix">The numbered suffix.</param> - private void PopulateBackdrops(BaseItem item, List<string> backdropFiles, string filename, string numberedSuffix) - { - var image = GetImage(item, filename); - - if (image != null) - { - backdropFiles.Add(image.FullName); - } - - var unfound = 0; - for (var i = 1; i <= 20; i++) - { - // Backdrop Image - image = GetImage(item, numberedSuffix + i); - - if (image != null) - { - backdropFiles.Add(image.FullName); - } - else - { - unfound++; - - if (unfound >= 3) - { - break; - } - } - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs b/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs deleted file mode 100644 index 20305006e..000000000 --- a/MediaBrowser.Controller/Providers/ImagesByNameProvider.cs +++ /dev/null @@ -1,184 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.IO; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers -{ - /// <summary> - /// Provides images for generic types by looking for standard images in the IBN - /// </summary> - public class ImagesByNameProvider : ImageFromMediaLocationProvider - { - public ImagesByNameProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - //only run for these generic types since we are expensive in file i/o - return item is IndexFolder || item is BasePluginFolder || item is CollectionFolder; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get - { - return MetadataProviderPriority.Last; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - // Force a refresh if the IBN path changed - if (providerInfo.Data != ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5()) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets a value indicating whether [refresh on file system stamp change]. - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return false; - } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - // If the IBN location exists return the last modified date of any file in it - var location = GetLocation(item); - - var directoryInfo = new DirectoryInfo(location); - - if (!directoryInfo.Exists) - { - return DateTime.MinValue; - } - - var files = directoryInfo.EnumerateFiles().ToList(); - - if (files.Count == 0) - { - return DateTime.MinValue; - } - - return files.Select(f => - { - var lastWriteTime = FileSystem.GetLastWriteTimeUtc(f, Logger); - var creationTime = FileSystem.GetCreationTimeUtc(f, Logger); - - return creationTime > lastWriteTime ? creationTime : lastWriteTime; - - }).Max(); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - var result = await base.FetchAsync(item, force, cancellationToken).ConfigureAwait(false); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = ConfigurationManager.ApplicationPaths.ItemsByNamePath.GetMD5(); - SetLastRefreshed(item, DateTime.UtcNow); - - return result; - } - - /// <summary> - /// Gets the location. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - protected string GetLocation(BaseItem item) - { - var name = FileSystem.GetValidFilename(item.Name); - - return Path.Combine(ConfigurationManager.ApplicationPaths.GeneralPath, name); - } - - /// <summary> - /// Gets the image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="filenameWithoutExtension">The filename without extension.</param> - /// <returns>FileSystemInfo.</returns> - protected override FileSystemInfo GetImage(BaseItem item, string filenameWithoutExtension) - { - var location = GetLocation(item); - - var directoryInfo = new DirectoryInfo(location); - - if (!directoryInfo.Exists) - { - return null; - } - - var files = directoryInfo.EnumerateFiles("*", SearchOption.TopDirectoryOnly).ToList(); - - var file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".png", StringComparison.OrdinalIgnoreCase)); - - if (file != null) - { - return file; - } - - file = files.FirstOrDefault(i => string.Equals(i.Name, filenameWithoutExtension + ".jpg", StringComparison.OrdinalIgnoreCase)); - - if (file != null) - { - return file; - } - - return null; - } - } -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs deleted file mode 100644 index 9fd67f477..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/AudioImageProvider.cs +++ /dev/null @@ -1,208 +0,0 @@ -using System.IO; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Concurrent; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - /// <summary> - /// Uses ffmpeg to create video images - /// </summary> - public class AudioImageProvider : BaseMetadataProvider - { - /// <summary> - /// Gets or sets the image cache. - /// </summary> - /// <value>The image cache.</value> - public FileSystemRepository ImageCache { get; set; } - - /// <summary> - /// The _locks - /// </summary> - private readonly ConcurrentDictionary<string, SemaphoreSlim> _locks = new ConcurrentDictionary<string, SemaphoreSlim>(); - - /// <summary> - /// The _media encoder - /// </summary> - private readonly IMediaEncoder _mediaEncoder; - - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// Initializes a new instance of the <see cref="BaseMetadataProvider" /> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="libraryManager">The library manager.</param> - /// <param name="mediaEncoder">The media encoder.</param> - public AudioImageProvider(ILogManager logManager, IServerConfigurationManager configurationManager, ILibraryManager libraryManager, IMediaEncoder mediaEncoder) - : base(logManager, configurationManager) - { - _libraryManager = libraryManager; - _mediaEncoder = mediaEncoder; - - ImageCache = new FileSystemRepository(Kernel.Instance.FFMpegManager.AudioImagesDataPath); - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item.LocationType == LocationType.FileSystem && item is Audio; - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - return item.DateModified; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Last; } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - var audio = (Audio)item; - - if (string.IsNullOrEmpty(audio.PrimaryImagePath) && audio.MediaStreams.Any(s => s.Type == MediaStreamType.Video)) - { - try - { - await CreateImagesForSong(audio, cancellationToken).ConfigureAwait(false); - } - catch (Exception ex) - { - Logger.ErrorException("Error extracting image for {0}", ex, item.Name); - } - } - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Creates the images for song. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.InvalidOperationException">Can't extract an image unless the audio file has an embedded image.</exception> - private async Task CreateImagesForSong(Audio item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var album = item.Parent as MusicAlbum; - - var filename = item.Album ?? string.Empty; - filename += item.Artist ?? string.Empty; - filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks; - - var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); - - if (!File.Exists(path)) - { - var semaphore = GetLock(path); - - // Acquire a lock - await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - // Check again - if (!File.Exists(path)) - { - try - { - var parentPath = Path.GetDirectoryName(path); - - if (!Directory.Exists(parentPath)) - { - Directory.CreateDirectory(parentPath); - } - - await _mediaEncoder.ExtractImage(new[] { item.Path }, InputType.AudioFile, null, path, cancellationToken).ConfigureAwait(false); - } - finally - { - semaphore.Release(); - } - } - else - { - semaphore.Release(); - } - } - - // Image is already in the cache - item.PrimaryImagePath = path; - - await _libraryManager.UpdateItem(item, cancellationToken).ConfigureAwait(false); - } - - /// <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)); - } - } -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs deleted file mode 100644 index 919c2ae21..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFMpegProvider.cs +++ /dev/null @@ -1,76 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - /// <summary> - /// Class BaseFFMpegProvider - /// </summary> - /// <typeparam name="T"></typeparam> - public abstract class BaseFFMpegProvider<T> : BaseMetadataProvider - where T : BaseItem - { - protected readonly IMediaEncoder MediaEncoder; - - protected BaseFFMpegProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder) : base(logManager, configurationManager) - { - MediaEncoder = mediaEncoder; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item.LocationType == LocationType.FileSystem && item is T; - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - return item.DateModified; - } - - /// <summary> - /// The null mount task result - /// </summary> - protected readonly Task<IIsoMount> NullMountTaskResult = Task.FromResult<IIsoMount>(null); - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return MediaEncoder.Version; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFProbeProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/BaseFFProbeProvider.cs deleted file mode 100644 index 4f40cffa1..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/BaseFFProbeProvider.cs +++ /dev/null @@ -1,358 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - /// <summary> - /// Provides a base class for extracting media information through ffprobe - /// </summary> - /// <typeparam name="T"></typeparam> - public abstract class BaseFFProbeProvider<T> : BaseFFMpegProvider<T> - where T : BaseItem - { - protected BaseFFProbeProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, IJsonSerializer jsonSerializer) - : base(logManager, configurationManager, mediaEncoder) - { - JsonSerializer = jsonSerializer; - } - - protected readonly IJsonSerializer JsonSerializer; - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - // Give this second priority - // Give metadata xml providers a chance to fill in data first, so that we can skip this whenever possible - get { return MetadataProviderPriority.Second; } - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - var myItem = (T)item; - - var isoMount = await MountIsoIfNeeded(myItem, cancellationToken).ConfigureAwait(false); - - try - { - OnPreFetch(myItem, isoMount); - - var result = await GetMediaInfo(item, isoMount, cancellationToken).ConfigureAwait(false); - - cancellationToken.ThrowIfCancellationRequested(); - - NormalizeFFProbeResult(result); - - cancellationToken.ThrowIfCancellationRequested(); - - Fetch(myItem, cancellationToken, result, isoMount); - - var video = myItem as Video; - - if (video != null) - { - await Kernel.Instance.FFMpegManager.PopulateChapterImages(video, cancellationToken, false, false).ConfigureAwait(false); - } - - SetLastRefreshed(item, DateTime.UtcNow); - } - finally - { - if (isoMount != null) - { - isoMount.Dispose(); - } - } - - return true; - } - - /// <summary> - /// Gets the media info. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="isoMount">The iso mount.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{MediaInfoResult}.</returns> - /// <exception cref="System.ArgumentNullException">inputPath - /// or - /// cache</exception> - private async Task<MediaInfoResult> GetMediaInfo(BaseItem item, IIsoMount isoMount, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var type = InputType.AudioFile; - var inputPath = isoMount == null ? new[] { item.Path } : new[] { isoMount.MountedPath }; - - var video = item as Video; - - if (video != null) - { - inputPath = MediaEncoderHelpers.GetInputArgument(video, isoMount, out type); - } - - return await MediaEncoder.GetMediaInfo(inputPath, type, cancellationToken).ConfigureAwait(false); - } - - /// <summary> - /// Mounts the iso if needed. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>IsoMount.</returns> - protected virtual Task<IIsoMount> MountIsoIfNeeded(T item, CancellationToken cancellationToken) - { - return NullMountTaskResult; - } - - /// <summary> - /// Called when [pre fetch]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="mount">The mount.</param> - protected virtual void OnPreFetch(T item, IIsoMount mount) - { - - } - - /// <summary> - /// Normalizes the FF probe result. - /// </summary> - /// <param name="result">The result.</param> - private void NormalizeFFProbeResult(MediaInfoResult result) - { - if (result.format != null && result.format.tags != null) - { - result.format.tags = ConvertDictionaryToCaseInSensitive(result.format.tags); - } - - if (result.streams != null) - { - // Convert all dictionaries to case insensitive - foreach (var stream in result.streams) - { - if (stream.tags != null) - { - stream.tags = ConvertDictionaryToCaseInSensitive(stream.tags); - } - - if (stream.disposition != null) - { - stream.disposition = ConvertDictionaryToCaseInSensitive(stream.disposition); - } - } - } - } - - /// <summary> - /// Subclasses must set item values using this - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="result">The result.</param> - /// <param name="isoMount">The iso mount.</param> - /// <returns>Task.</returns> - protected abstract void Fetch(T item, CancellationToken cancellationToken, MediaInfoResult result, IIsoMount isoMount); - - /// <summary> - /// Converts ffprobe stream info to our MediaStream class - /// </summary> - /// <param name="streamInfo">The stream info.</param> - /// <param name="formatInfo">The format info.</param> - /// <returns>MediaStream.</returns> - protected MediaStream GetMediaStream(MediaStreamInfo streamInfo, MediaFormatInfo formatInfo) - { - var stream = new MediaStream - { - Codec = streamInfo.codec_name, - Language = GetDictionaryValue(streamInfo.tags, "language"), - Profile = streamInfo.profile, - Level = streamInfo.level, - Index = streamInfo.index - }; - - if (streamInfo.codec_type.Equals("audio", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = MediaStreamType.Audio; - - stream.Channels = streamInfo.channels; - - if (!string.IsNullOrEmpty(streamInfo.sample_rate)) - { - stream.SampleRate = int.Parse(streamInfo.sample_rate, UsCulture); - } - } - else if (streamInfo.codec_type.Equals("subtitle", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = MediaStreamType.Subtitle; - } - else if (streamInfo.codec_type.Equals("video", StringComparison.OrdinalIgnoreCase)) - { - stream.Type = MediaStreamType.Video; - - stream.Width = streamInfo.width; - stream.Height = streamInfo.height; - stream.PixelFormat = streamInfo.pix_fmt; - stream.AspectRatio = streamInfo.display_aspect_ratio; - - stream.AverageFrameRate = GetFrameRate(streamInfo.avg_frame_rate); - stream.RealFrameRate = GetFrameRate(streamInfo.r_frame_rate); - } - else - { - return null; - } - - // Get stream bitrate - if (stream.Type != MediaStreamType.Subtitle) - { - if (!string.IsNullOrEmpty(streamInfo.bit_rate)) - { - stream.BitRate = int.Parse(streamInfo.bit_rate, UsCulture); - } - else if (formatInfo != null && !string.IsNullOrEmpty(formatInfo.bit_rate)) - { - // If the stream info doesn't have a bitrate get the value from the media format info - stream.BitRate = int.Parse(formatInfo.bit_rate, UsCulture); - } - } - - if (streamInfo.disposition != null) - { - var isDefault = GetDictionaryValue(streamInfo.disposition, "default"); - var isForced = GetDictionaryValue(streamInfo.disposition, "forced"); - - stream.IsDefault = string.Equals(isDefault, "1", StringComparison.OrdinalIgnoreCase); - - stream.IsForced = string.Equals(isForced, "1", StringComparison.OrdinalIgnoreCase); - } - - return stream; - } - - /// <summary> - /// Gets a frame rate from a string value in ffprobe output - /// This could be a number or in the format of 2997/125. - /// </summary> - /// <param name="value">The value.</param> - /// <returns>System.Nullable{System.Single}.</returns> - private float? GetFrameRate(string value) - { - if (!string.IsNullOrEmpty(value)) - { - var parts = value.Split('/'); - - float result; - - if (parts.Length == 2) - { - result = float.Parse(parts[0], UsCulture) / float.Parse(parts[1], UsCulture); - } - else - { - result = float.Parse(parts[0], UsCulture); - } - - return float.IsNaN(result) ? (float?)null : result; - } - - return null; - } - - /// <summary> - /// Gets a string from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.String.</returns> - protected string GetDictionaryValue(Dictionary<string, string> tags, string key) - { - if (tags == null) - { - return null; - } - - string val; - - tags.TryGetValue(key, out val); - return val; - } - - /// <summary> - /// Gets an int from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.Nullable{System.Int32}.</returns> - protected int? GetDictionaryNumericValue(Dictionary<string, string> tags, string key) - { - var val = GetDictionaryValue(tags, key); - - if (!string.IsNullOrEmpty(val)) - { - int i; - - if (int.TryParse(val, out i)) - { - return i; - } - } - - return null; - } - - /// <summary> - /// Gets a DateTime from an FFProbeResult tags dictionary - /// </summary> - /// <param name="tags">The tags.</param> - /// <param name="key">The key.</param> - /// <returns>System.Nullable{DateTime}.</returns> - protected DateTime? GetDictionaryDateTime(Dictionary<string, string> tags, string key) - { - var val = GetDictionaryValue(tags, key); - - if (!string.IsNullOrEmpty(val)) - { - DateTime i; - - if (DateTime.TryParse(val, out i)) - { - return i.ToUniversalTime(); - } - } - - return null; - } - - /// <summary> - /// Converts a dictionary to case insensitive - /// </summary> - /// <param name="dict">The dict.</param> - /// <returns>Dictionary{System.StringSystem.String}.</returns> - private Dictionary<string, string> ConvertDictionaryToCaseInSensitive(Dictionary<string, string> dict) - { - return new Dictionary<string, string>(dict, StringComparer.OrdinalIgnoreCase); - } - } -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs deleted file mode 100644 index f234611bd..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs +++ /dev/null @@ -1,223 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - /// <summary> - /// Extracts audio information using ffprobe - /// </summary> - public class FFProbeAudioInfoProvider : BaseFFProbeProvider<Audio> - { - public FFProbeAudioInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, IJsonSerializer jsonSerializer) - : base(logManager, configurationManager, mediaEncoder, jsonSerializer) - { - } - - /// <summary> - /// Fetches the specified audio. - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="data">The data.</param> - /// <param name="isoMount">The iso mount.</param> - /// <returns>Task.</returns> - protected override void Fetch(Audio audio, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount) - { - if (data.streams == null) - { - Logger.Error("Audio item has no streams: " + audio.Path); - return; - } - - audio.MediaStreams = data.streams.Select(s => GetMediaStream(s, data.format)) - .Where(i => i != null) - .ToList(); - - // Get the first audio stream - var stream = data.streams.FirstOrDefault(s => s.codec_type.Equals("audio", StringComparison.OrdinalIgnoreCase)); - - if (stream != null) - { - // Get duration from stream properties - var duration = stream.duration; - - // If it's not there go into format properties - if (string.IsNullOrEmpty(duration)) - { - duration = data.format.duration; - } - - // If we got something, parse it - if (!string.IsNullOrEmpty(duration)) - { - audio.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(duration, UsCulture)).Ticks; - } - } - - if (data.format.tags != null) - { - FetchDataFromTags(audio, data.format.tags); - } - } - - /// <summary> - /// Fetches data from the tags dictionary - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - private void FetchDataFromTags(Audio audio, Dictionary<string, string> tags) - { - var title = GetDictionaryValue(tags, "title"); - - // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(title)) - { - audio.Name = title; - } - - var composer = GetDictionaryValue(tags, "composer"); - - if (!string.IsNullOrWhiteSpace(composer)) - { - foreach (var person in Split(composer)) - { - var name = person.Trim(); - - if (!string.IsNullOrEmpty(name)) - { - audio.AddPerson(new PersonInfo { Name = name, Type = PersonType.Composer }); - } - } - } - - audio.Album = GetDictionaryValue(tags, "album"); - - audio.Artist = GetDictionaryValue(tags, "artist"); - - // Several different forms of albumartist - audio.AlbumArtist = GetDictionaryValue(tags, "albumartist") ?? GetDictionaryValue(tags, "album artist") ?? GetDictionaryValue(tags, "album_artist"); - - // Track number - audio.IndexNumber = GetDictionaryNumericValue(tags, "track"); - - // Disc number - audio.ParentIndexNumber = GetDictionaryDiscValue(tags); - - audio.Language = GetDictionaryValue(tags, "language"); - - audio.ProductionYear = GetDictionaryNumericValue(tags, "date"); - - // Several different forms of retaildate - audio.PremiereDate = GetDictionaryDateTime(tags, "retaildate") ?? GetDictionaryDateTime(tags, "retail date") ?? GetDictionaryDateTime(tags, "retail_date"); - - // If we don't have a ProductionYear try and get it from PremiereDate - if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) - { - audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; - } - - FetchGenres(audio, tags); - - // There's several values in tags may or may not be present - FetchStudios(audio, tags, "organization"); - FetchStudios(audio, tags, "ensemble"); - FetchStudios(audio, tags, "publisher"); - } - - /// <summary> - /// Splits the specified val. - /// </summary> - /// <param name="val">The val.</param> - /// <returns>System.String[][].</returns> - private IEnumerable<string> Split(string val) - { - // Only use the comma as a delimeter if there are no slashes or pipes. - // We want to be careful not to split names that have commas in them - var delimeter = val.IndexOf('/') == -1 && val.IndexOf('|') == -1 ? new[] { ',' } : new[] { '/', '|' }; - - return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries); - } - - /// <summary> - /// Gets the studios from the tags collection - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - /// <param name="tagName">Name of the tag.</param> - private void FetchStudios(Audio audio, Dictionary<string, string> tags, string tagName) - { - var val = GetDictionaryValue(tags, tagName); - - if (!string.IsNullOrEmpty(val)) - { - var studios = - val.Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.Equals(i, audio.Artist, StringComparison.OrdinalIgnoreCase) && !string.Equals(i, audio.AlbumArtist, StringComparison.OrdinalIgnoreCase)); - - audio.Studios.Clear(); - - foreach (var studio in studios) - { - audio.AddStudio(studio); - } - } - } - - /// <summary> - /// Gets the genres from the tags collection - /// </summary> - /// <param name="audio">The audio.</param> - /// <param name="tags">The tags.</param> - private void FetchGenres(Audio audio, Dictionary<string, string> tags) - { - var val = GetDictionaryValue(tags, "genre"); - - if (!string.IsNullOrEmpty(val)) - { - audio.Genres.Clear(); - - foreach (var genre in val - .Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i))) - { - audio.AddGenre(genre); - } - } - } - - /// <summary> - /// Gets the disc number, which is sometimes can be in the form of '1', or '1/3' - /// </summary> - /// <param name="tags">The tags.</param> - /// <returns>System.Nullable{System.Int32}.</returns> - private int? GetDictionaryDiscValue(Dictionary<string, string> tags) - { - var disc = GetDictionaryValue(tags, "disc"); - - if (!string.IsNullOrEmpty(disc)) - { - disc = disc.Split('/')[0]; - - int num; - - if (int.TryParse(disc, out num)) - { - return num; - } - } - - return null; - } - } - -} diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs deleted file mode 100644 index d4e5a30c6..000000000 --- a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeVideoInfoProvider.cs +++ /dev/null @@ -1,439 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Common.MediaInfo; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Controller.Localization; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.MediaInfo; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.MediaInfo -{ - /// <summary> - /// Extracts video information using ffprobe - /// </summary> - public class FFProbeVideoInfoProvider : BaseFFProbeProvider<Video> - { - public FFProbeVideoInfoProvider(IIsoManager isoManager, IBlurayExaminer blurayExaminer, IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IMediaEncoder mediaEncoder, ILocalizationManager localization) - : base(logManager, configurationManager, mediaEncoder, jsonSerializer) - { - if (isoManager == null) - { - throw new ArgumentNullException("isoManager"); - } - if (blurayExaminer == null) - { - throw new ArgumentNullException("blurayExaminer"); - } - - _blurayExaminer = blurayExaminer; - _localization = localization; - _isoManager = isoManager; - } - - /// <summary> - /// Gets or sets the bluray examiner. - /// </summary> - /// <value>The bluray examiner.</value> - private readonly IBlurayExaminer _blurayExaminer; - - /// <summary> - /// The _iso manager - /// </summary> - private readonly IIsoManager _isoManager; - - private readonly ILocalizationManager _localization; - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Supports video files and dvd structures - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - if (item.LocationType != LocationType.FileSystem) - { - return false; - } - - var video = item as Video; - - if (video != null) - { - if (video.VideoType == VideoType.Iso) - { - return _isoManager.CanMount(item.Path); - } - - return video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Dvd || video.VideoType == VideoType.BluRay; - } - - return false; - } - - /// <summary> - /// Called when [pre fetch]. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="mount">The mount.</param> - protected override void OnPreFetch(Video item, IIsoMount mount) - { - if (item.VideoType == VideoType.Iso) - { - item.IsoType = DetermineIsoType(mount); - } - - if (item.VideoType == VideoType.Dvd || (item.IsoType.HasValue && item.IsoType == IsoType.Dvd)) - { - PopulateDvdStreamFiles(item, mount); - } - - base.OnPreFetch(item, mount); - } - - /// <summary> - /// Mounts the iso if needed. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>IsoMount.</returns> - protected override Task<IIsoMount> MountIsoIfNeeded(Video item, CancellationToken cancellationToken) - { - if (item.VideoType == VideoType.Iso) - { - return _isoManager.Mount(item.Path, cancellationToken); - } - - return base.MountIsoIfNeeded(item, cancellationToken); - } - - /// <summary> - /// Determines the type of the iso. - /// </summary> - /// <param name="isoMount">The iso mount.</param> - /// <returns>System.Nullable{IsoType}.</returns> - private IsoType? DetermineIsoType(IIsoMount isoMount) - { - var folders = Directory.EnumerateDirectories(isoMount.MountedPath).Select(Path.GetFileName).ToList(); - - if (folders.Contains("video_ts", StringComparer.OrdinalIgnoreCase)) - { - return IsoType.Dvd; - } - if (folders.Contains("bdmv", StringComparer.OrdinalIgnoreCase)) - { - return IsoType.BluRay; - } - - return null; - } - - /// <summary> - /// Finds vob files and populates the dvd stream file properties - /// </summary> - /// <param name="video">The video.</param> - /// <param name="isoMount">The iso mount.</param> - private void PopulateDvdStreamFiles(Video video, IIsoMount isoMount) - { - // min size 300 mb - const long minPlayableSize = 314572800; - - var root = isoMount != null ? isoMount.MountedPath : video.Path; - - // Try to eliminate menus and intros by skipping all files at the front of the list that are less than the minimum size - // Once we reach a file that is at least the minimum, return all subsequent ones - var files = Directory.EnumerateFiles(root, "*.vob", SearchOption.AllDirectories).SkipWhile(f => new FileInfo(f).Length < minPlayableSize).ToList(); - - // Assuming they're named "vts_05_01", take all files whose second part matches that of the first file - if (files.Count > 0) - { - var parts = Path.GetFileNameWithoutExtension(files[0]).Split('_'); - - if (parts.Length == 3) - { - var title = parts[1]; - - files = files.TakeWhile(f => - { - var fileParts = Path.GetFileNameWithoutExtension(f).Split('_'); - - return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase); - - }).ToList(); - } - } - - video.PlayableStreamFileNames = files.Select(Path.GetFileName).ToList(); - } - - /// <summary> - /// Fetches the specified video. - /// </summary> - /// <param name="video">The video.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="data">The data.</param> - /// <param name="isoMount">The iso mount.</param> - /// <returns>Task.</returns> - protected override void Fetch(Video video, CancellationToken cancellationToken, MediaInfoResult data, IIsoMount isoMount) - { - if (data.format != null) - { - // For dvd's this may not always be accurate, so don't set the runtime if the item already has one - var needToSetRuntime = video.VideoType != VideoType.Dvd || video.RunTimeTicks == null || video.RunTimeTicks.Value == 0; - - if (needToSetRuntime && !string.IsNullOrEmpty(data.format.duration)) - { - video.RunTimeTicks = TimeSpan.FromSeconds(double.Parse(data.format.duration, UsCulture)).Ticks; - } - } - - if (data.streams != null) - { - video.MediaStreams = data.streams.Select(s => GetMediaStream(s, data.format)) - .Where(i => i != null) - .ToList(); - } - - if (data.Chapters != null) - { - video.Chapters = data.Chapters; - } - - if (video.Chapters == null || video.Chapters.Count == 0) - { - AddDummyChapters(video); - } - - if (video.VideoType == VideoType.BluRay || (video.IsoType.HasValue && video.IsoType.Value == IsoType.BluRay)) - { - var inputPath = isoMount != null ? isoMount.MountedPath : video.Path; - FetchBdInfo(video, inputPath, cancellationToken); - } - - AddExternalSubtitles(video); - } - - /// <summary> - /// Adds the external subtitles. - /// </summary> - /// <param name="video">The video.</param> - private void AddExternalSubtitles(Video video) - { - var useParent = (video.VideoType == VideoType.VideoFile || video.VideoType == VideoType.Iso) && !(video is Movie) && !(video is MusicVideo); - - if (useParent && video.Parent == null) - { - return; - } - - var fileSystemChildren = useParent - ? video.Parent.ResolveArgs.FileSystemChildren - : video.ResolveArgs.FileSystemChildren; - - var startIndex = video.MediaStreams == null ? 0 : video.MediaStreams.Count; - var streams = new List<MediaStream>(); - - var videoFileNameWithoutExtension = Path.GetFileNameWithoutExtension(video.Path); - - foreach (var file in fileSystemChildren - .Where(f => !f.Attributes.HasFlag(FileAttributes.Directory) && string.Equals(Path.GetExtension(f.FullName), ".srt", StringComparison.OrdinalIgnoreCase))) - { - var fullName = file.FullName; - - var fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fullName); - - // If the subtitle file matches the video file name - if (string.Equals(videoFileNameWithoutExtension, fileNameWithoutExtension, StringComparison.OrdinalIgnoreCase)) - { - streams.Add(new MediaStream - { - Index = startIndex++, - Type = MediaStreamType.Subtitle, - IsExternal = true, - Path = fullName, - Codec = "srt" - }); - } - else if (fileNameWithoutExtension.StartsWith(videoFileNameWithoutExtension + ".", StringComparison.OrdinalIgnoreCase)) - { - // Support xbmc naming conventions - 300.spanish.srt - var language = fileNameWithoutExtension.Split('.').LastOrDefault(); - - // 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)); - - if (culture != null) - { - language = culture.ThreeLetterISOLanguageName; - } - - streams.Add(new MediaStream - { - Index = startIndex++, - Type = MediaStreamType.Subtitle, - IsExternal = true, - Path = fullName, - Codec = "srt", - Language = language - }); - } - } - - if (video.MediaStreams == null) - { - video.MediaStreams = new List<MediaStream>(); - } - video.MediaStreams.AddRange(streams); - } - - /// <summary> - /// The dummy chapter duration - /// </summary> - private readonly long _dummyChapterDuration = TimeSpan.FromMinutes(5).Ticks; - - /// <summary> - /// Adds the dummy chapters. - /// </summary> - /// <param name="video">The video.</param> - private void AddDummyChapters(Video video) - { - var runtime = video.RunTimeTicks ?? 0; - - if (runtime < _dummyChapterDuration) - { - return; - } - - long currentChapterTicks = 0; - var index = 1; - - var chapters = new List<ChapterInfo>(); - - while (currentChapterTicks < runtime) - { - chapters.Add(new ChapterInfo - { - Name = "Chapter " + index, - StartPositionTicks = currentChapterTicks - }); - - index++; - currentChapterTicks += _dummyChapterDuration; - } - - video.Chapters = chapters; - } - - /// <summary> - /// Fetches the bd info. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="inputPath">The input path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - private void FetchBdInfo(BaseItem item, string inputPath, CancellationToken cancellationToken) - { - var video = (Video)item; - - var result = GetBDInfo(inputPath); - - cancellationToken.ThrowIfCancellationRequested(); - - int? currentHeight = null; - int? currentWidth = null; - int? currentBitRate = null; - - var videoStream = video.MediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); - - // Grab the values that ffprobe recorded - if (videoStream != null) - { - currentBitRate = videoStream.BitRate; - currentWidth = videoStream.Width; - currentHeight = videoStream.Height; - } - - // Fill video properties from the BDInfo result - Fetch(video, result); - - videoStream = video.MediaStreams.FirstOrDefault(s => s.Type == MediaStreamType.Video); - - // Use the ffprobe values if these are empty - if (videoStream != null) - { - videoStream.BitRate = IsEmpty(videoStream.BitRate) ? currentBitRate : videoStream.BitRate; - videoStream.Width = IsEmpty(videoStream.Width) ? currentWidth : videoStream.Width; - videoStream.Height = IsEmpty(videoStream.Height) ? currentHeight : videoStream.Height; - } - } - - /// <summary> - /// Determines whether the specified num is empty. - /// </summary> - /// <param name="num">The num.</param> - /// <returns><c>true</c> if the specified num is empty; otherwise, <c>false</c>.</returns> - private bool IsEmpty(int? num) - { - return !num.HasValue || num.Value == 0; - } - - /// <summary> - /// Fills video properties from the VideoStream of the largest playlist - /// </summary> - /// <param name="video">The video.</param> - /// <param name="stream">The stream.</param> - private void Fetch(Video video, BlurayDiscInfo stream) - { - // Check all input for null/empty/zero - - video.MediaStreams = stream.MediaStreams; - - if (stream.RunTimeTicks.HasValue && stream.RunTimeTicks.Value > 0) - { - video.RunTimeTicks = stream.RunTimeTicks; - } - - video.PlayableStreamFileNames = stream.Files.ToList(); - - if (stream.Chapters != null) - { - video.Chapters = stream.Chapters.Select(c => new ChapterInfo - { - StartPositionTicks = TimeSpan.FromSeconds(c).Ticks - - }).ToList(); - } - } - - /// <summary> - /// Gets information about the longest playlist on a bdrom - /// </summary> - /// <param name="path">The path.</param> - /// <returns>VideoStream.</returns> - private BlurayDiscInfo GetBDInfo(string path) - { - return _blurayExaminer.GetDiscInfo(path); - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs deleted file mode 100644 index f1e670862..000000000 --- a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs +++ /dev/null @@ -1,400 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class FanArtMovieProvider - /// </summary> - class FanArtMovieProvider : FanartBaseProvider - { - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// The us culture - /// </summary> - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - internal static FanArtMovieProvider Current { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="FanArtMovieProvider" /> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <exception cref="System.ArgumentNullException">httpClient</exception> - public FanArtMovieProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; - _providerManager = providerManager; - Current = this; - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "13"; - } - } - - /// <summary> - /// The fan art base URL - /// </summary> - protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/movie/{0}/{1}/xml/all/1/1"; - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb))) - { - return false; - } - - if (!ConfigurationManager.Configuration.DownloadMovieImages.Art && - !ConfigurationManager.Configuration.DownloadMovieImages.Logo && - !ConfigurationManager.Configuration.DownloadMovieImages.Disc && - !ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && - !ConfigurationManager.Configuration.DownloadMovieImages.Banner && - !ConfigurationManager.Configuration.DownloadMovieImages.Thumb) - { - return false; - } - - if (item.HasImage(ImageType.Art) && - item.HasImage(ImageType.Logo) && - item.HasImage(ImageType.Disc) && - item.HasImage(ImageType.Banner) && - item.HasImage(ImageType.Thumb) && - item.BackdropImagePaths.Count > 0) - { - return false; - } - - // Refresh if tmdb id has changed - if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb))) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="id">The id.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(string id) - { - if (!string.IsNullOrEmpty(id)) - { - // Process images - var path = GetMovieDataPath(ConfigurationManager.ApplicationPaths, id); - - var files = new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.FullName + i.LastWriteTimeUtc.Ticks) - .ToArray(); - - if (files.Length > 0) - { - return string.Join(string.Empty, files).GetMD5(); - } - } - - return Guid.Empty; - } - - /// <summary> - /// Gets the movie data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="tmdbId">The TMDB id.</param> - /// <returns>System.String.</returns> - internal static string GetMovieDataPath(IApplicationPaths appPaths, string tmdbId) - { - var dataPath = Path.Combine(GetMoviesDataPath(appPaths), tmdbId); - - if (!Directory.Exists(dataPath)) - { - Directory.CreateDirectory(dataPath); - } - - return dataPath; - } - - /// <summary> - /// Gets the movie data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <returns>System.String.</returns> - internal static string GetMoviesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.DataPath, "fanart-movies"); - - if (!Directory.Exists(dataPath)) - { - Directory.CreateDirectory(dataPath); - } - - return dataPath; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - var movieId = item.GetProviderId(MetadataProviders.Tmdb); - - var movieDataPath = GetMovieDataPath(ConfigurationManager.ApplicationPaths, movieId); - var xmlPath = Path.Combine(movieDataPath, "fanart.xml"); - - // Only download the xml if it doesn't already exist. The prescan task will take care of getting updates - if (!File.Exists(xmlPath)) - { - await DownloadMovieXml(movieDataPath, movieId, cancellationToken).ConfigureAwait(false); - } - - if (File.Exists(xmlPath)) - { - await FetchFromXml(item, xmlPath, cancellationToken).ConfigureAwait(false); - } - - data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb)); - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Downloads the movie XML. - /// </summary> - /// <param name="movieDataPath">The movie data path.</param> - /// <param name="tmdbId">The TMDB id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - internal async Task DownloadMovieXml(string movieDataPath, string tmdbId, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - string url = string.Format(FanArtBaseUrl, ApiKey, tmdbId); - - var xmlPath = Path.Combine(movieDataPath, "fanart.xml"); - - using (var response = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } - } - } - - /// <summary> - /// Fetches from XML. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="xmlFilePath">The XML file path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchFromXml(BaseItem item, string xmlFilePath, CancellationToken cancellationToken) - { - var doc = new XmlDocument(); - doc.Load(xmlFilePath); - - var language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower(); - - cancellationToken.ThrowIfCancellationRequested(); - - var saveLocal = ConfigurationManager.Configuration.SaveLocalMeta && - item.LocationType == LocationType.FileSystem; - - string path; - var hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hd" : ""; - - if (ConfigurationManager.Configuration.DownloadMovieImages.Logo && !item.HasImage(ImageType.Logo)) - { - var node = - doc.SelectSingleNode("//fanart/movie/movielogos/" + hd + "movielogo[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/movielogos/movielogo[@lang = \"" + language + "\"]/@url"); - if (node == null && language != "en") - { - //maybe just couldn't find language - try just first one - node = doc.SelectSingleNode("//fanart/movie/movielogos/" + hd + "movielogo/@url"); - } - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMovieImages.Art && !item.HasImage(ImageType.Art)) - { - var node = - doc.SelectSingleNode("//fanart/movie/moviearts/" + hd + "movieart[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviearts/" + hd + "movieart/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviearts/movieart[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviearts/movieart/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMovieImages.Disc && !item.HasImage(ImageType.Disc)) - { - var node = doc.SelectSingleNode("//fanart/movie/moviediscs/moviedisc[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviediscs/moviedisc/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(item, path, DiscFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMovieImages.Banner && !item.HasImage(ImageType.Banner)) - { - var node = doc.SelectSingleNode("//fanart/movie/moviebanners/moviebanner[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviebanners/moviebanner/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMovieImages.Thumb && !item.HasImage(ImageType.Thumb)) - { - var node = doc.SelectSingleNode("//fanart/movie/moviethumbs/moviethumb[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/movie/moviethumbs/moviethumb/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(item, path, ThumbFile, saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - if (ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && item.BackdropImagePaths.Count == 0) - { - var nodes = doc.SelectNodes("//fanart/movie/moviebackgrounds//@url"); - - if (nodes != null) - { - var numBackdrops = item.BackdropImagePaths.Count; - - foreach (XmlNode node in nodes) - { - path = node.Value; - - if (!string.IsNullOrEmpty(path)) - { - item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), saveLocal, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - - numBackdrops++; - - if (item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) break; - } - } - - } - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/FanArtMovieUpdatesPrescanTask.cs b/MediaBrowser.Controller/Providers/Movies/FanArtMovieUpdatesPrescanTask.cs deleted file mode 100644 index b8efddfb4..000000000 --- a/MediaBrowser.Controller/Providers/Movies/FanArtMovieUpdatesPrescanTask.cs +++ /dev/null @@ -1,185 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers.Music; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - class FanArtMovieUpdatesPrescanTask : ILibraryPrescanTask - { - private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmovies/{0}/{1}/"; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - private readonly IJsonSerializer _jsonSerializer; - - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public FanArtMovieUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) - { - _jsonSerializer = jsonSerializer; - _config = config; - _logger = logger; - _httpClient = httpClient; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - - var path = FanArtMovieProvider.GetMoviesDataPath(_config.CommonApplicationPaths); - - var timestampFile = Path.Combine(path, "time.txt"); - - var timestampFileInfo = new FileInfo(timestampFile); - - // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) - { - return; - } - - // Find out the last time we queried for updates - var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty; - - var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList(); - - // If this is our first time, don't do any updates and just record the timestamp - if (!string.IsNullOrEmpty(lastUpdateTime)) - { - var moviesToUpdate = await GetMovieIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false); - - progress.Report(5); - - await UpdateMovies(moviesToUpdate, path, progress, cancellationToken).ConfigureAwait(false); - } - - var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture); - - File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8); - - progress.Report(100); - } - - private async Task<IEnumerable<string>> GetMovieIdsToUpdate(IEnumerable<string> existingIds, string lastUpdateTime, CancellationToken cancellationToken) - { - // First get last time - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = string.Format(UpdatesUrl, FanartBaseProvider.ApiKey, lastUpdateTime), - CancellationToken = cancellationToken, - EnableHttpCompression = true, - ResourcePool = FanartBaseProvider.FanArtResourcePool - - }).ConfigureAwait(false)) - { - // If empty fanart will return a string of "null", rather than an empty list - using (var reader = new StreamReader(stream)) - { - var json = await reader.ReadToEndAsync().ConfigureAwait(false); - - if (string.Equals(json, "null", StringComparison.OrdinalIgnoreCase)) - { - return new List<string>(); - } - - var updates = _jsonSerializer.DeserializeFromString<List<FanArtUpdatesPrescanTask.FanArtUpdate>>(json); - - return updates.Select(i => i.id).Where(i => existingIds.Contains(i, StringComparer.OrdinalIgnoreCase)); - } - } - } - - private async Task UpdateMovies(IEnumerable<string> idList, string moviesDataPath, IProgress<double> progress, CancellationToken cancellationToken) - { - var list = idList.ToList(); - var numComplete = 0; - - foreach (var id in list) - { - try - { - await UpdateMovie(id, moviesDataPath, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - // Already logged at lower levels, but don't fail the whole operation, unless something other than a timeout - if (!ex.IsTimedOut) - { - throw; - } - } - - numComplete++; - double percent = numComplete; - percent /= list.Count; - percent *= 95; - - progress.Report(percent + 5); - } - } - - private Task UpdateMovie(string tmdbId, string movieDataPath, CancellationToken cancellationToken) - { - _logger.Info("Updating movie " + tmdbId); - - movieDataPath = Path.Combine(movieDataPath, tmdbId); - - if (!Directory.Exists(movieDataPath)) - { - Directory.CreateDirectory(movieDataPath); - } - - return FanArtMovieProvider.Current.DownloadMovieXml(movieDataPath, tmdbId, cancellationToken); - } - - /// <summary> - /// Dates the time to unix timestamp. - /// </summary> - /// <param name="dateTime">The date time.</param> - /// <returns>System.Double.</returns> - private static double DateTimeToUnixTimestamp(DateTime dateTime) - { - return (dateTime - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds; - } - - public class FanArtUpdate - { - public string id { get; set; } - public string name { get; set; } - public string new_images { get; set; } - public string total_images { get; set; } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbImagesProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbImagesProvider.cs deleted file mode 100644 index 5a30f447b..000000000 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbImagesProvider.cs +++ /dev/null @@ -1,412 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class MovieDbImagesProvider - /// </summary> - public class MovieDbImagesProvider : BaseMetadataProvider - { - /// <summary> - /// The get images - /// </summary> - private const string GetImages = @"http://api.themoviedb.org/3/{2}/{0}/images?api_key={1}"; - - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// The _json serializer - /// </summary> - private readonly IJsonSerializer _jsonSerializer; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - - /// <summary> - /// Initializes a new instance of the <see cref="MovieDbImagesProvider"/> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - public MovieDbImagesProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) - : base(logManager, configurationManager) - { - _providerManager = providerManager; - _jsonSerializer = jsonSerializer; - _httpClient = httpClient; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Fourth; } - } - - /// <summary> - /// Supports the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - // Don't support local trailers - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// If we save locally, refresh if they delete something - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "3"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb))) - { - return false; - } - - // Refresh if tmdb id has changed - if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb))) - { - return true; - } - - // Don't refresh if we already have both poster and backdrop and we're not refreshing images - if (item.HasImage(ImageType.Primary) && item.BackdropImagePaths.Count > 0) - { - return false; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - var images = await FetchImages(item, item.GetProviderId(MetadataProviders.Tmdb), cancellationToken).ConfigureAwait(false); - - var status = await ProcessImages(item, images, cancellationToken).ConfigureAwait(false); - - data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Tmdb)); - - SetLastRefreshed(item, DateTime.UtcNow, status); - return true; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <returns>Guid.</returns> - private Guid GetComparisonData(string id) - { - return string.IsNullOrEmpty(id) ? Guid.Empty : id.GetMD5(); - } - - /// <summary> - /// Fetches the images. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="id">The id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{MovieImages}.</returns> - private async Task<MovieImages> FetchImages(BaseItem item, string id, CancellationToken cancellationToken) - { - using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = string.Format(GetImages, id, MovieDbProvider.ApiKey, item is BoxSet ? "collection" : "movie"), - CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader - - }).ConfigureAwait(false)) - { - return _jsonSerializer.DeserializeFromStream<MovieImages>(json); - } - } - - /// <summary> - /// Processes the images. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="images">The images.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task.</returns> - protected virtual async Task<ProviderRefreshStatus> ProcessImages(BaseItem item, MovieImages images, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var status = ProviderRefreshStatus.Success; - - // poster - if (images.posters != null && images.posters.Count > 0 && !item.HasImage(ImageType.Primary)) - { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedPosterSize; - // get highest rated poster for our language - - var postersSortedByVote = images.posters.OrderByDescending(i => i.vote_average); - - var poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 != null && p.iso_639_1.Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, StringComparison.OrdinalIgnoreCase)); - if (poster == null && !ConfigurationManager.Configuration.PreferredMetadataLanguage.Equals("en")) - { - // couldn't find our specific language, find english (if that wasn't our language) - poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 != null && p.iso_639_1.Equals("en", StringComparison.OrdinalIgnoreCase)); - } - if (poster == null) - { - //still couldn't find it - try highest rated null one - poster = postersSortedByVote.FirstOrDefault(p => p.iso_639_1 == null); - } - if (poster == null) - { - //finally - just get the highest rated one - poster = postersSortedByVote.FirstOrDefault(); - } - if (poster != null) - { - var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = tmdbImageUrl + poster.file_path, - CancellationToken = cancellationToken - - }).ConfigureAwait(false); - - item.PrimaryImagePath = await _providerManager.SaveImage(item, img, "folder" + Path.GetExtension(poster.file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - // backdrops - only download if earlier providers didn't find any (fanart) - if (images.backdrops != null && images.backdrops.Count > 0 && ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && item.BackdropImagePaths.Count == 0) - { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var tmdbImageUrl = tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedBackdropSize; - - for (var i = 0; i < images.backdrops.Count; i++) - { - var bdName = "backdrop" + (i == 0 ? "" : i.ToString(CultureInfo.InvariantCulture)); - - var hasLocalBackdrop = item.LocationType == LocationType.FileSystem && ConfigurationManager.Configuration.SaveLocalMeta ? item.HasLocalImage(bdName) : item.BackdropImagePaths.Count > i; - - if (!hasLocalBackdrop) - { - var img = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = tmdbImageUrl + images.backdrops[i].file_path, - CancellationToken = cancellationToken - - }).ConfigureAwait(false); - - item.BackdropImagePaths.Add(await _providerManager.SaveImage(item, img, bdName + Path.GetExtension(images.backdrops[i].file_path), ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem, cancellationToken).ConfigureAwait(false)); - } - - if (item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) - { - break; - } - } - } - - return status; - } - - /// <summary> - /// Class Backdrop - /// </summary> - protected class Backdrop - { - /// <summary> - /// Gets or sets the file_path. - /// </summary> - /// <value>The file_path.</value> - public string file_path { get; set; } - /// <summary> - /// Gets or sets the width. - /// </summary> - /// <value>The width.</value> - public int width { get; set; } - /// <summary> - /// Gets or sets the height. - /// </summary> - /// <value>The height.</value> - public int height { get; set; } - /// <summary> - /// Gets or sets the iso_639_1. - /// </summary> - /// <value>The iso_639_1.</value> - public string iso_639_1 { get; set; } - /// <summary> - /// Gets or sets the aspect_ratio. - /// </summary> - /// <value>The aspect_ratio.</value> - public double aspect_ratio { get; set; } - /// <summary> - /// Gets or sets the vote_average. - /// </summary> - /// <value>The vote_average.</value> - public double vote_average { get; set; } - /// <summary> - /// Gets or sets the vote_count. - /// </summary> - /// <value>The vote_count.</value> - public int vote_count { get; set; } - } - - /// <summary> - /// Class Poster - /// </summary> - protected class Poster - { - /// <summary> - /// Gets or sets the file_path. - /// </summary> - /// <value>The file_path.</value> - public string file_path { get; set; } - /// <summary> - /// Gets or sets the width. - /// </summary> - /// <value>The width.</value> - public int width { get; set; } - /// <summary> - /// Gets or sets the height. - /// </summary> - /// <value>The height.</value> - public int height { get; set; } - /// <summary> - /// Gets or sets the iso_639_1. - /// </summary> - /// <value>The iso_639_1.</value> - public string iso_639_1 { get; set; } - /// <summary> - /// Gets or sets the aspect_ratio. - /// </summary> - /// <value>The aspect_ratio.</value> - public double aspect_ratio { get; set; } - /// <summary> - /// Gets or sets the vote_average. - /// </summary> - /// <value>The vote_average.</value> - public double vote_average { get; set; } - /// <summary> - /// Gets or sets the vote_count. - /// </summary> - /// <value>The vote_count.</value> - public int vote_count { get; set; } - } - - /// <summary> - /// Class MovieImages - /// </summary> - protected class MovieImages - { - /// <summary> - /// Gets or sets the backdrops. - /// </summary> - /// <value>The backdrops.</value> - public List<Backdrop> backdrops { get; set; } - /// <summary> - /// Gets or sets the posters. - /// </summary> - /// <value>The posters.</value> - public List<Poster> posters { get; set; } - } - - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs deleted file mode 100644 index 6a9806d84..000000000 --- a/MediaBrowser.Controller/Providers/Movies/MovieDbProvider.cs +++ /dev/null @@ -1,1270 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class MovieDbProvider - /// </summary> - public class MovieDbProvider : BaseMetadataProvider, IDisposable - { - protected static CultureInfo EnUs = new CultureInfo("en-US"); - - protected readonly IProviderManager ProviderManager; - - /// <summary> - /// The movie db - /// </summary> - private readonly SemaphoreSlim _movieDbResourcePool = new SemaphoreSlim(1,1); - - internal static MovieDbProvider Current { get; private set; } - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="MovieDbProvider" /> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="providerManager">The provider manager.</param> - public MovieDbProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager) - : base(logManager, configurationManager) - { - JsonSerializer = jsonSerializer; - HttpClient = httpClient; - ProviderManager = providerManager; - Current = this; - } - - /// <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) - { - _movieDbResourcePool.Dispose(); - } - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - // Don't support local trailers - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// If we save locally, refresh if they delete something - /// </summary> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - protected override string ProviderVersion - { - get - { - return "2"; - } - } - - /// <summary> - /// The _TMDB settings task - /// </summary> - private TmdbSettingsResult _tmdbSettings; - - private readonly SemaphoreSlim _tmdbSettingsSemaphore = new SemaphoreSlim(1, 1); - - /// <summary> - /// Gets the TMDB settings. - /// </summary> - /// <returns>Task{TmdbSettingsResult}.</returns> - internal async Task<TmdbSettingsResult> GetTmdbSettings(CancellationToken cancellationToken) - { - if (_tmdbSettings != null) - { - return _tmdbSettings; - } - - await _tmdbSettingsSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false); - - // Check again in case it got populated while we were waiting. - if (_tmdbSettings != null) - { - _tmdbSettingsSemaphore.Release(); - return _tmdbSettings; - } - - try - { - using (var json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = string.Format(TmdbConfigUrl, ApiKey), - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - _tmdbSettings = JsonSerializer.DeserializeFromStream<TmdbSettingsResult>(json); - - return _tmdbSettings; - } - } - finally - { - _tmdbSettingsSemaphore.Release(); - } - } - - /// <summary> - /// The json provider - /// </summary> - protected MovieProviderFromJson JsonProvider; - - /// <summary> - /// Sets the persisted last refresh date on the item for this provider. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="value">The value.</param> - /// <param name="providerVersion">The provider version.</param> - /// <param name="status">The status.</param> - public override void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success) - { - base.SetLastRefreshed(item, value, providerVersion, status); - - if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) - { - //in addition to ours, we need to set the last refreshed time for the local data provider - //so it won't see the new files we download and process them all over again - if (JsonProvider == null) JsonProvider = new MovieProviderFromJson(LogManager, ConfigurationManager, JsonSerializer, HttpClient, ProviderManager); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(JsonProvider.Id, out data)) - { - data = new BaseProviderInfo(); - } - - data.LastRefreshed = value; - item.ProviderData[JsonProvider.Id] = data; - } - } - - private const string TmdbConfigUrl = "http://api.themoviedb.org/3/configuration?api_key={0}"; - private const string Search3 = @"http://api.themoviedb.org/3/search/movie?api_key={1}&query={0}&language={2}"; - private const string AltTitleSearch = @"http://api.themoviedb.org/3/movie/{0}/alternative_titles?api_key={1}&country={2}"; - private const string GetMovieInfo3 = @"http://api.themoviedb.org/3/movie/{0}?api_key={1}&language={2}&append_to_response=casts,releases,images,keywords"; - private const string GetBoxSetInfo3 = @"http://api.themoviedb.org/3/collection/{0}?api_key={1}&language={2}&append_to_response=images"; - - internal static string ApiKey = "f6bd687ffa63cd282b6ff2c6877f2669"; - internal static string AcceptHeader = "application/json,image/*"; - - static readonly Regex[] NameMatches = new[] { - new Regex(@"(?<name>.*)\((?<year>\d{4})\)"), // matches "My Movie (2001)" and gives us the name and the year - new Regex(@"(?<name>.*)") // last resort matches the whole string as the name - }; - - public const string LocalMetaFileName = "tmdb3.json"; - public const string AltMetaFileName = "movie.xml"; - - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (HasAltMeta(item)) - return false; //never refresh if has meta from other source - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - if (HasAltMeta(item)) - { - Logger.Info("MovieDbProvider - Not fetching because 3rd party meta exists for " + item.Name); - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - cancellationToken.ThrowIfCancellationRequested(); - - await FetchMovieData(item, cancellationToken).ConfigureAwait(false); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Determines whether [has local meta] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if [has local meta] [the specified item]; otherwise, <c>false</c>.</returns> - private bool HasLocalMeta(BaseItem item) - { - //need at least the xml and folder.jpg/png or a movie.xml put in by someone else - return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName); - } - - /// <summary> - /// Determines whether [has alt meta] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if [has alt meta] [the specified item]; otherwise, <c>false</c>.</returns> - private bool HasAltMeta(BaseItem item) - { - return item.LocationType == LocationType.FileSystem && item.ResolveArgs.ContainsMetaFileByName(AltMetaFileName); - } - - /// <summary> - /// Fetches the movie data. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken"></param> - /// <returns>Task.</returns> - private async Task FetchMovieData(BaseItem item, CancellationToken cancellationToken) - { - string id = item.GetProviderId(MetadataProviders.Tmdb) ?? await FindId(item, item.ProductionYear, cancellationToken).ConfigureAwait(false); - if (id != null) - { - Logger.Debug("MovieDbProvider - getting movie info with id: " + id); - - cancellationToken.ThrowIfCancellationRequested(); - - await FetchMovieData(item, id, cancellationToken).ConfigureAwait(false); - } - else - { - Logger.Info("MovieDBProvider could not find " + item.Name + ". Check name on themoviedb.org."); - } - } - - /// <summary> - /// Parses the name. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="justName">Name of the just.</param> - /// <param name="year">The year.</param> - protected void ParseName(string name, out string justName, out int? year) - { - justName = null; - year = null; - foreach (var re in NameMatches) - { - Match m = re.Match(name); - if (m.Success) - { - justName = m.Groups["name"].Value.Trim(); - string y = m.Groups["year"] != null ? m.Groups["year"].Value : null; - int temp; - year = Int32.TryParse(y, out temp) ? temp : (int?)null; - break; - } - } - } - - /// <summary> - /// Finds the id. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="productionYear">The production year.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.String}.</returns> - public async Task<string> FindId(BaseItem item, int? productionYear, CancellationToken cancellationToken) - { - string id = null; - - if (item.LocationType == LocationType.FileSystem) - { - string justName = item.Path != null ? item.Path.Substring(item.Path.LastIndexOf(Path.DirectorySeparatorChar)) : string.Empty; - id = justName.GetAttributeValue("tmdbid"); - if (id != null) - { - Logger.Debug("Using tmdb id specified in path."); - return id; - } - } - - int? year; - string name = item.Name; - ParseName(name, out name, out year); - - if (year == null && productionYear != null) - { - year = productionYear; - } - - Logger.Info("MovieDbProvider: Finding id for movie: " + name); - string language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower(); - - //if we are a boxset - look at our first child - var boxset = item as BoxSet; - if (boxset != null) - { - var firstChild = boxset.Children.FirstOrDefault(); - - if (firstChild != null) - { - Logger.Debug("MovieDbProvider - Attempting to find boxset ID from: " + firstChild.Name); - string childName; - int? childYear; - ParseName(firstChild.Name, out childName, out childYear); - id = await GetBoxsetIdFromMovie(childName, childYear, language, cancellationToken).ConfigureAwait(false); - if (id != null) - { - Logger.Info("MovieDbProvider - Found Boxset ID: " + id); - } - } - - return id; - } - //nope - search for it - id = await AttemptFindId(name, year, language, cancellationToken).ConfigureAwait(false); - if (id == null) - { - //try in english if wasn't before - if (language != "en") - { - id = await AttemptFindId(name, year, "en", cancellationToken).ConfigureAwait(false); - } - else - { - // try with dot and _ turned to space - var originalName = name; - - name = name.Replace(",", " "); - name = name.Replace(".", " "); - name = name.Replace("_", " "); - name = name.Replace("-", ""); - - // Search again if the new name is different - if (!string.Equals(name, originalName)) - { - id = await AttemptFindId(name, year, language, cancellationToken).ConfigureAwait(false); - - if (id == null && language != "en") - { - //one more time, in english - id = await AttemptFindId(name, year, "en", cancellationToken).ConfigureAwait(false); - - } - } - - if (id == null && item.LocationType == LocationType.FileSystem) - { - //last resort - try using the actual folder name - var pathName = Path.GetFileName(item.ResolveArgs.Path); - - // Only search if it's a name we haven't already tried. - if (!string.Equals(pathName, name, StringComparison.OrdinalIgnoreCase) - && !string.Equals(pathName, originalName, StringComparison.OrdinalIgnoreCase)) - { - id = await AttemptFindId(pathName, year, "en", cancellationToken).ConfigureAwait(false); - } - } - } - } - - return id; - } - - /// <summary> - /// Attempts the find id. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="year">The year.</param> - /// <param name="language">The language.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.String}.</returns> - public virtual async Task<string> AttemptFindId(string name, int? year, string language, CancellationToken cancellationToken) - { - string url3 = string.Format(Search3, UrlEncode(name), ApiKey, language); - TmdbMovieSearchResults searchResult = null; - - using (Stream json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - searchResult = JsonSerializer.DeserializeFromStream<TmdbMovieSearchResults>(json); - } - - if (searchResult == null || searchResult.results.Count == 0) - { - //try replacing numbers - foreach (var pair in ReplaceStartNumbers) - { - if (name.StartsWith(pair.Key)) - { - name = name.Remove(0, pair.Key.Length); - name = pair.Value + name; - } - } - foreach (var pair in ReplaceEndNumbers) - { - if (name.EndsWith(pair.Key)) - { - name = name.Remove(name.IndexOf(pair.Key), pair.Key.Length); - name = name + pair.Value; - } - } - Logger.Info("MovieDBProvider - No results. Trying replacement numbers: " + name); - url3 = string.Format(Search3, UrlEncode(name), ApiKey, language); - - using (var json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - searchResult = JsonSerializer.DeserializeFromStream<TmdbMovieSearchResults>(json); - } - } - if (searchResult != null) - { - string compName = GetComparableName(name, Logger); - foreach (var possible in searchResult.results) - { - string matchedName = null; - string id = possible.id.ToString(CultureInfo.InvariantCulture); - string n = possible.title; - if (GetComparableName(n, Logger) == compName) - { - matchedName = n; - } - else - { - n = possible.original_title; - if (GetComparableName(n, Logger) == compName) - { - matchedName = n; - } - } - - Logger.Debug("MovieDbProvider - " + compName + " didn't match " + n); - //if main title matches we don't have to look for alternatives - if (matchedName == null) - { - //that title didn't match - look for alternatives - url3 = string.Format(AltTitleSearch, id, ApiKey, ConfigurationManager.Configuration.MetadataCountryCode); - - using (var json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url3, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - var response = JsonSerializer.DeserializeFromStream<TmdbAltTitleResults>(json); - - if (response != null && response.titles != null) - { - foreach (var title in response.titles) - { - var t = GetComparableName(title.title, Logger); - if (t == compName) - { - Logger.Debug("MovieDbProvider - " + compName + - " matched " + t); - matchedName = t; - break; - } - Logger.Debug("MovieDbProvider - " + compName + - " did not match " + t); - } - } - } - } - - if (matchedName != null) - { - Logger.Debug("Match " + matchedName + " for " + name); - if (year != null) - { - DateTime r; - - //These dates are always in this exact format - if (DateTime.TryParseExact(possible.release_date, "yyyy-MM-dd", EnUs, DateTimeStyles.None, out r)) - { - if (Math.Abs(r.Year - year.Value) > 1) // allow a 1 year tolerance on release date - { - Logger.Debug("Result " + matchedName + " released on " + r + " did not match year " + year); - continue; - } - } - } - //matched name and year - return id; - } - - } - } - - return null; - } - - /// <summary> - /// URLs the encode. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>System.String.</returns> - private static string UrlEncode(string name) - { - return WebUtility.UrlEncode(name); - } - - /// <summary> - /// Gets the boxset id from movie. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="year">The year.</param> - /// <param name="language">The language.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.String}.</returns> - protected async Task<string> GetBoxsetIdFromMovie(string name, int? year, string language, CancellationToken cancellationToken) - { - string id = null; - string childId = await AttemptFindId(name, year, language, cancellationToken).ConfigureAwait(false); - if (childId != null) - { - string url = string.Format(GetMovieInfo3, childId, ApiKey, language); - - using (Stream json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - var movieResult = JsonSerializer.DeserializeFromStream<CompleteMovieData>(json); - - if (movieResult != null && movieResult.belongs_to_collection != null) - { - id = movieResult.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture); - } - else - { - Logger.Error("Unable to obtain boxset id."); - } - } - } - return id; - } - - /// <summary> - /// Fetches the movie data. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="id">The id.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task.</returns> - protected async Task FetchMovieData(BaseItem item, string id, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (String.IsNullOrEmpty(id)) - { - Logger.Info("MoviedbProvider: Ignoring " + item.Name + " because ID forced blank."); - return; - } - if (item.GetProviderId(MetadataProviders.Tmdb) == null) item.SetProviderId(MetadataProviders.Tmdb, id); - - var mainResult = await FetchMainResult(item, id, cancellationToken).ConfigureAwait(false); - - if (mainResult == null) return; - - ProcessMainInfo(item, mainResult); - - cancellationToken.ThrowIfCancellationRequested(); - - //and save locally - if (ConfigurationManager.Configuration.SaveLocalMeta && item.LocationType == LocationType.FileSystem) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(mainResult, ms); - - cancellationToken.ThrowIfCancellationRequested(); - - await ProviderManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - } - } - - /// <summary> - /// Fetches the main result. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="id">The id.</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{CompleteMovieData}.</returns> - protected async Task<CompleteMovieData> FetchMainResult(BaseItem item, string id, CancellationToken cancellationToken) - { - var baseUrl = item is BoxSet ? GetBoxSetInfo3 : GetMovieInfo3; - - string url = string.Format(baseUrl, id, ApiKey, ConfigurationManager.Configuration.PreferredMetadataLanguage); - CompleteMovieData mainResult; - - cancellationToken.ThrowIfCancellationRequested(); - - using (var json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - mainResult = JsonSerializer.DeserializeFromStream<CompleteMovieData>(json); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (mainResult != null && string.IsNullOrEmpty(mainResult.overview)) - { - if (ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower() != "en") - { - Logger.Info("MovieDbProvider couldn't find meta for language " + ConfigurationManager.Configuration.PreferredMetadataLanguage + ". Trying English..."); - - url = string.Format(baseUrl, id, ApiKey, "en"); - - using (Stream json = await GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = AcceptHeader - - }).ConfigureAwait(false)) - { - mainResult = JsonSerializer.DeserializeFromStream<CompleteMovieData>(json); - } - - if (String.IsNullOrEmpty(mainResult.overview)) - { - Logger.Error("MovieDbProvider - Unable to find information for " + item.Name + " (id:" + id + ")"); - return null; - } - } - } - return mainResult; - } - - /// <summary> - /// Processes the main info. - /// </summary> - /// <param name="movie">The movie.</param> - /// <param name="movieData">The movie data.</param> - protected virtual void ProcessMainInfo(BaseItem movie, CompleteMovieData movieData) - { - if (movie != null && movieData != null) - { - - movie.Name = movieData.title ?? movieData.original_title ?? movie.Name; - movie.Overview = movieData.overview; - movie.Overview = movie.Overview != null ? movie.Overview.Replace("\n\n", "\n") : null; - movie.HomePageUrl = movieData.homepage; - movie.Budget = movieData.budget; - movie.Revenue = movieData.revenue; - - if (!string.IsNullOrEmpty(movieData.tagline)) - { - movie.Taglines.Clear(); - movie.AddTagline(movieData.tagline); - } - - movie.SetProviderId(MetadataProviders.Imdb, movieData.imdb_id); - - if (movieData.belongs_to_collection != null) - { - movie.SetProviderId(MetadataProviders.TmdbCollection, movieData.belongs_to_collection.id.ToString(CultureInfo.InvariantCulture)); - } - - float rating; - string voteAvg = movieData.vote_average.ToString(CultureInfo.InvariantCulture); - //tmdb appears to have unified their numbers to always report "7.3" regardless of country - // so I removed the culture-specific processing here because it was not working for other countries -ebr - if (float.TryParse(voteAvg, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out rating)) - movie.CommunityRating = rating; - - //release date and certification are retrieved based on configured country and we fall back on US if not there - if (movieData.releases != null && movieData.releases.countries != null) - { - var ourRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals(ConfigurationManager.Configuration.MetadataCountryCode, StringComparison.OrdinalIgnoreCase)) ?? new Country(); - var usRelease = movieData.releases.countries.FirstOrDefault(c => c.iso_3166_1.Equals("US", StringComparison.OrdinalIgnoreCase)) ?? new Country(); - var ratingPrefix = ConfigurationManager.Configuration.MetadataCountryCode.Equals("us", StringComparison.OrdinalIgnoreCase) ? "" : ConfigurationManager.Configuration.MetadataCountryCode +"-"; - movie.OfficialRating = !string.IsNullOrEmpty(ourRelease.certification) ? ratingPrefix + ourRelease.certification : !string.IsNullOrEmpty(usRelease.certification) ? usRelease.certification : null; - - if (ourRelease.release_date > new DateTime(1900, 1, 1)) - { - if (ourRelease.release_date.Year != 1) - { - movie.PremiereDate = ourRelease.release_date.ToUniversalTime(); - movie.ProductionYear = ourRelease.release_date.Year; - } - } - else - { - if (usRelease.release_date.Year != 1) - { - movie.PremiereDate = usRelease.release_date.ToUniversalTime(); - movie.ProductionYear = usRelease.release_date.Year; - } - } - } - else - { - if (movieData.release_date.Year != 1) - { - //no specific country release info at all - movie.PremiereDate = movieData.release_date.ToUniversalTime(); - movie.ProductionYear = movieData.release_date.Year; - } - } - - //if that didn't find a rating and we are a boxset, use the one from our first child - if (movie.OfficialRating == null && movie is BoxSet) - { - var boxset = movie as BoxSet; - Logger.Info("MovieDbProvider - Using rating of first child of boxset..."); - - var firstChild = boxset.Children.FirstOrDefault(); - - boxset.OfficialRating = firstChild != null ? firstChild.OfficialRating : null; - } - - if (movieData.runtime > 0) - movie.OriginalRunTimeTicks = TimeSpan.FromMinutes(movieData.runtime).Ticks; - - //studios - if (movieData.production_companies != null) - { - movie.Studios.Clear(); - - foreach (var studio in movieData.production_companies.Select(c => c.name)) - { - movie.AddStudio(studio); - } - } - - //genres - if (movieData.genres != null) - { - movie.Genres.Clear(); - - foreach (var genre in movieData.genres.Select(g => g.name)) - { - movie.AddGenre(genre); - } - } - - movie.People.Clear(); - movie.Tags.Clear(); - - //Actors, Directors, Writers - all in People - //actors come from cast - if (movieData.casts != null && movieData.casts.cast != null) - { - foreach (var actor in movieData.casts.cast.OrderBy(a => a.order)) movie.AddPerson(new PersonInfo { Name = actor.name, Role = actor.character, Type = PersonType.Actor }); - } - - //and the rest from crew - if (movieData.casts != null && movieData.casts.crew != null) - { - foreach (var person in movieData.casts.crew) movie.AddPerson(new PersonInfo { Name = person.name, Role = person.job, Type = person.department }); - } - - if (movieData.keywords != null && movieData.keywords.keywords != null) - { - movie.Tags = movieData.keywords.keywords.Select(i => i.name).ToList(); - } - } - - } - - private DateTime _lastRequestDate = DateTime.MinValue; - - /// <summary> - /// Gets the movie db response. - /// </summary> - internal async Task<Stream> GetMovieDbResponse(HttpRequestOptions options) - { - var cancellationToken = options.CancellationToken; - - await _movieDbResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - // Limit to three requests per second - var diff = 340 - (DateTime.Now - _lastRequestDate).TotalMilliseconds; - - if (diff > 0) - { - await Task.Delay(Convert.ToInt32(diff), cancellationToken).ConfigureAwait(false); - } - - _lastRequestDate = DateTime.Now; - - return await HttpClient.Get(options).ConfigureAwait(false); - } - finally - { - _lastRequestDate = DateTime.Now; - - _movieDbResourcePool.Release(); - } - } - - /// <summary> - /// The remove - /// </summary> - const string Remove = "\"'!`?"; - // "Face/Off" support. - /// <summary> - /// The spacers - /// </summary> - const string Spacers = "/,.:;\\(){}[]+-_=–*"; // (there are not actually two - in the they are different char codes) - /// <summary> - /// The replace start numbers - /// </summary> - static readonly Dictionary<string, string> ReplaceStartNumbers = new Dictionary<string, string> { - {"1 ","one "}, - {"2 ","two "}, - {"3 ","three "}, - {"4 ","four "}, - {"5 ","five "}, - {"6 ","six "}, - {"7 ","seven "}, - {"8 ","eight "}, - {"9 ","nine "}, - {"10 ","ten "}, - {"11 ","eleven "}, - {"12 ","twelve "}, - {"13 ","thirteen "}, - {"100 ","one hundred "}, - {"101 ","one hundred one "} - }; - - /// <summary> - /// The replace end numbers - /// </summary> - static readonly Dictionary<string, string> ReplaceEndNumbers = new Dictionary<string, string> { - {" 1"," i"}, - {" 2"," ii"}, - {" 3"," iii"}, - {" 4"," iv"}, - {" 5"," v"}, - {" 6"," vi"}, - {" 7"," vii"}, - {" 8"," viii"}, - {" 9"," ix"}, - {" 10"," x"} - }; - - /// <summary> - /// Gets the name of the comparable. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="logger">The logger.</param> - /// <returns>System.String.</returns> - internal static string GetComparableName(string name, ILogger logger) - { - name = name.ToLower(); - name = name.Replace("á", "a"); - name = name.Replace("é", "e"); - name = name.Replace("í", "i"); - name = name.Replace("ó", "o"); - name = name.Replace("ú", "u"); - name = name.Replace("ü", "u"); - name = name.Replace("ñ", "n"); - foreach (var pair in ReplaceStartNumbers) - { - if (name.StartsWith(pair.Key)) - { - name = name.Remove(0, pair.Key.Length); - name = pair.Value + name; - logger.Info("MovieDbProvider - Replaced Start Numbers: " + name); - } - } - foreach (var pair in ReplaceEndNumbers) - { - if (name.EndsWith(pair.Key)) - { - name = name.Remove(name.IndexOf(pair.Key), pair.Key.Length); - name = name + pair.Value; - logger.Info("MovieDbProvider - Replaced End Numbers: " + name); - } - } - name = name.Normalize(NormalizationForm.FormKD); - var sb = new StringBuilder(); - foreach (var c in name) - { - if (c >= 0x2B0 && c <= 0x0333) - { - // skip char modifier and diacritics - } - else if (Remove.IndexOf(c) > -1) - { - // skip chars we are removing - } - else if (Spacers.IndexOf(c) > -1) - { - sb.Append(" "); - } - else if (c == '&') - { - sb.Append(" and "); - } - else - { - sb.Append(c); - } - } - name = sb.ToString(); - name = name.Replace(", the", ""); - name = name.Replace(" the ", " "); - name = name.Replace("the ", ""); - - string prev_name; - do - { - prev_name = name; - name = name.Replace(" ", " "); - } while (name.Length != prev_name.Length); - - return name.Trim(); - } - - #region Result Objects - - - /// <summary> - /// Class TmdbTitle - /// </summary> - protected class TmdbTitle - { - /// <summary> - /// Gets or sets the iso_3166_1. - /// </summary> - /// <value>The iso_3166_1.</value> - public string iso_3166_1 { get; set; } - /// <summary> - /// Gets or sets the title. - /// </summary> - /// <value>The title.</value> - public string title { get; set; } - } - - /// <summary> - /// Class TmdbAltTitleResults - /// </summary> - protected class TmdbAltTitleResults - { - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int id { get; set; } - /// <summary> - /// Gets or sets the titles. - /// </summary> - /// <value>The titles.</value> - public List<TmdbTitle> titles { get; set; } - } - - /// <summary> - /// Class TmdbMovieSearchResult - /// </summary> - protected class TmdbMovieSearchResult - { - /// <summary> - /// Gets or sets a value indicating whether this <see cref="TmdbMovieSearchResult" /> is adult. - /// </summary> - /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> - public bool adult { get; set; } - /// <summary> - /// Gets or sets the backdrop_path. - /// </summary> - /// <value>The backdrop_path.</value> - public string backdrop_path { get; set; } - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int id { get; set; } - /// <summary> - /// Gets or sets the original_title. - /// </summary> - /// <value>The original_title.</value> - public string original_title { get; set; } - /// <summary> - /// Gets or sets the release_date. - /// </summary> - /// <value>The release_date.</value> - public string release_date { get; set; } - /// <summary> - /// Gets or sets the poster_path. - /// </summary> - /// <value>The poster_path.</value> - public string poster_path { get; set; } - /// <summary> - /// Gets or sets the popularity. - /// </summary> - /// <value>The popularity.</value> - public double popularity { get; set; } - /// <summary> - /// Gets or sets the title. - /// </summary> - /// <value>The title.</value> - public string title { get; set; } - /// <summary> - /// Gets or sets the vote_average. - /// </summary> - /// <value>The vote_average.</value> - public double vote_average { get; set; } - /// <summary> - /// Gets or sets the vote_count. - /// </summary> - /// <value>The vote_count.</value> - public int vote_count { get; set; } - } - - /// <summary> - /// Class TmdbMovieSearchResults - /// </summary> - protected class TmdbMovieSearchResults - { - /// <summary> - /// Gets or sets the page. - /// </summary> - /// <value>The page.</value> - public int page { get; set; } - /// <summary> - /// Gets or sets the results. - /// </summary> - /// <value>The results.</value> - public List<TmdbMovieSearchResult> results { get; set; } - /// <summary> - /// Gets or sets the total_pages. - /// </summary> - /// <value>The total_pages.</value> - public int total_pages { get; set; } - /// <summary> - /// Gets or sets the total_results. - /// </summary> - /// <value>The total_results.</value> - public int total_results { get; set; } - } - - protected class BelongsToCollection - { - public int id { get; set; } - public string name { get; set; } - public string poster_path { get; set; } - public string backdrop_path { get; set; } - } - - protected class GenreItem - { - public int id { get; set; } - public string name { get; set; } - } - - protected class ProductionCompany - { - public string name { get; set; } - public int id { get; set; } - } - - protected class ProductionCountry - { - public string iso_3166_1 { get; set; } - public string name { get; set; } - } - - protected class SpokenLanguage - { - public string iso_639_1 { get; set; } - public string name { get; set; } - } - - protected class Cast - { - public int id { get; set; } - public string name { get; set; } - public string character { get; set; } - public int order { get; set; } - public int cast_id { get; set; } - public string profile_path { get; set; } - } - - protected class Crew - { - public int id { get; set; } - public string name { get; set; } - public string department { get; set; } - public string job { get; set; } - public string profile_path { get; set; } - } - - protected class Casts - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - protected class Country - { - public string iso_3166_1 { get; set; } - public string certification { get; set; } - public DateTime release_date { get; set; } - } - - protected class Releases - { - public List<Country> countries { get; set; } - } - - protected class Keyword - { - public int id { get; set; } - public string name { get; set; } - } - - protected class Keywords - { - public List<Keyword> keywords { get; set; } - } - - protected class CompleteMovieData - { - public bool adult { get; set; } - public string backdrop_path { get; set; } - public BelongsToCollection belongs_to_collection { get; set; } - public int budget { get; set; } - public List<GenreItem> genres { get; set; } - public string homepage { get; set; } - public int id { get; set; } - public string imdb_id { get; set; } - public string original_title { get; set; } - public string overview { get; set; } - public double popularity { get; set; } - public string poster_path { get; set; } - public List<ProductionCompany> production_companies { get; set; } - public List<ProductionCountry> production_countries { get; set; } - public DateTime release_date { get; set; } - public int revenue { get; set; } - public int runtime { get; set; } - public List<SpokenLanguage> spoken_languages { get; set; } - public string status { get; set; } - public string tagline { get; set; } - public string title { get; set; } - public double vote_average { get; set; } - public int vote_count { get; set; } - public Casts casts { get; set; } - public Releases releases { get; set; } - public Keywords keywords { get; set; } - } - - public class TmdbImageSettings - { - public List<string> backdrop_sizes { get; set; } - public string base_url { get; set; } - public List<string> poster_sizes { get; set; } - public List<string> profile_sizes { get; set; } - } - - public class TmdbSettingsResult - { - public TmdbImageSettings images { get; set; } - } - #endregion - - public void Dispose() - { - Dispose(true); - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs b/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs deleted file mode 100644 index bdd4f88d2..000000000 --- a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromJson.cs +++ /dev/null @@ -1,121 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class MovieProviderFromJson - /// </summary> - public class MovieProviderFromJson : MovieDbProvider - { - public MovieProviderFromJson(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient, IProviderManager providerManager) - : base(logManager, configurationManager, jsonSerializer, httpClient, providerManager) - { - } - - public override bool Supports(BaseItem item) - { - if (item.LocationType != LocationType.FileSystem) - { - return false; - } - - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get { return false; } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LocalMetaFileName)); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (item.ResolveArgs.ContainsMetaFileByName(AltMetaFileName)) - { - return false; // don't read our file if 3rd party data exists - } - - if (!item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName)) - { - return false; // nothing to read - } - - // Need to re-override to jump over intermediate implementation - return CompareDate(item) > providerInfo.LastRefreshed; - } - - /// <summary> - /// Fetches the async. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LocalMetaFileName)); - if (entry != null) - { - // read in our saved meta and pass to processing function - var movieData = JsonSerializer.DeserializeFromFile<CompleteMovieData>(entry.FullName); - - cancellationToken.ThrowIfCancellationRequested(); - - ProcessMainInfo(item, movieData); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - return FalseTaskResult; - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs b/MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs deleted file mode 100644 index 25fd94138..000000000 --- a/MediaBrowser.Controller/Providers/Movies/MovieProviderFromXml.cs +++ /dev/null @@ -1,113 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class MovieProviderFromXml - /// </summary> - public class MovieProviderFromXml : BaseMetadataProvider - { - public MovieProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - var trailer = item as Trailer; - - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - return item is Movie || item is BoxSet || item is MusicVideo; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "movie.xml")); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - return Fetch(item, cancellationToken); - } - - /// <summary> - /// Fetches the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "movie.xml")); - - if (metadataFile != null) - { - var path = metadataFile.FullName; - var boxset = item as BoxSet; - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - if (boxset != null) - { - new BaseItemXmlParser<BoxSet>(Logger).Fetch(boxset, path, cancellationToken); - } - else - { - new BaseItemXmlParser<Movie>(Logger).Fetch((Movie)item, path, cancellationToken); - } - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - return false; - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs deleted file mode 100644 index 855e527bf..000000000 --- a/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs +++ /dev/null @@ -1,225 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Movies; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Globalization; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - public class OpenMovieDatabaseProvider : BaseMetadataProvider - { - private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(2, 2); - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - public OpenMovieDatabaseProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IJsonSerializer jsonSerializer, IHttpClient httpClient) - : base(logManager, configurationManager) - { - JsonSerializer = jsonSerializer; - HttpClient = httpClient; - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "6"; - } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Supports the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - var trailer = item as Trailer; - - // Don't support local trailers - if (trailer != null) - { - return !trailer.IsLocalTrailer; - } - - return item is Movie || item is MusicVideo; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="imdbId">The imdb id.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(string imdbId) - { - return string.IsNullOrEmpty(imdbId) ? Guid.Empty : imdbId.GetMD5(); - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get - { - // Run after moviedb and xml providers - return MetadataProviderPriority.Last; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - // Refresh if imdb id has changed - if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Imdb))) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - var imdbId = item.GetProviderId(MetadataProviders.Imdb); - - if (string.IsNullOrEmpty(imdbId)) - { - data.Data = GetComparisonData(imdbId); - data.LastRefreshStatus = ProviderRefreshStatus.Success; - return true; - } - - var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId; - - var url = string.Format("http://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam); - - using (var stream = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = _resourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - var result = JsonSerializer.DeserializeFromStream<RootObject>(stream); - - int tomatoMeter; - - if (!string.IsNullOrEmpty(result.tomatoMeter) && int.TryParse(result.tomatoMeter, NumberStyles.Integer, UsCulture, out tomatoMeter)) - { - item.CriticRating = tomatoMeter; - } - - if (!string.IsNullOrEmpty(result.tomatoConsensus) - && !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase) - && !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase)) - { - item.CriticRatingSummary = result.tomatoConsensus; - } - } - - data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Imdb)); - data.LastRefreshStatus = ProviderRefreshStatus.Success; - SetLastRefreshed(item, DateTime.UtcNow); - - return true; - } - - - protected class RootObject - { - public string Title { get; set; } - public string Year { get; set; } - public string Rated { get; set; } - public string Released { get; set; } - public string Runtime { get; set; } - public string Genre { get; set; } - public string Director { get; set; } - public string Writer { get; set; } - public string Actors { get; set; } - public string Plot { get; set; } - public string Poster { get; set; } - public string imdbRating { get; set; } - public string imdbVotes { get; set; } - public string imdbID { get; set; } - public string Type { get; set; } - public string tomatoMeter { get; set; } - public string tomatoImage { get; set; } - public string tomatoRating { get; set; } - public string tomatoReviews { get; set; } - public string tomatoFresh { get; set; } - public string tomatoRotten { get; set; } - public string tomatoConsensus { get; set; } - public string tomatoUserMeter { get; set; } - public string tomatoUserRating { get; set; } - public string tomatoUserReviews { get; set; } - public string DVD { get; set; } - public string BoxOffice { get; set; } - public string Production { get; set; } - public string Website { get; set; } - public string Response { get; set; } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs b/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs deleted file mode 100644 index da53e316e..000000000 --- a/MediaBrowser.Controller/Providers/Movies/PersonProviderFromJson.cs +++ /dev/null @@ -1,114 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class PersonProviderFromJson - /// </summary> - class PersonProviderFromJson : TmdbPersonProvider - { - public PersonProviderFromJson(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(jsonSerializer, logManager, configurationManager, providerManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Person; - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return false; - } - } - - // Need to re-override to jump over intermediate implementation - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (!item.ResolveArgs.ContainsMetaFileByName(MetaFileName)) - { - return false; - } - - return CompareDate(item) > providerInfo.LastRefreshed; - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation,MetaFileName)); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get - { - return MetadataProviderPriority.Third; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - var personInfo = JsonSerializer.DeserializeFromFile<PersonResult>(Path.Combine(item.MetaLocation, MetaFileName)); - - cancellationToken.ThrowIfCancellationRequested(); - - ProcessInfo((Person)item, personInfo); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - catch (FileNotFoundException) - { - // This is okay - just means we force refreshed and there isn't a json file - return FalseTaskResult; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs b/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs deleted file mode 100644 index dd9d8353c..000000000 --- a/MediaBrowser.Controller/Providers/Movies/TmdbPersonProvider.cs +++ /dev/null @@ -1,435 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Movies -{ - /// <summary> - /// Class TmdbPersonProvider - /// </summary> - public class TmdbPersonProvider : BaseMetadataProvider - { - /// <summary> - /// The meta file name - /// </summary> - protected const string MetaFileName = "tmdb3.json"; - - protected readonly IProviderManager ProviderManager; - - public TmdbPersonProvider(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - JsonSerializer = jsonSerializer; - ProviderManager = providerManager; - } - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Person; - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - protected override string ProviderVersion - { - get - { - return "2"; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var person = (Person)item; - - var id = person.GetProviderId(MetadataProviders.Tmdb); - - // We don't already have an Id, need to fetch it - if (string.IsNullOrEmpty(id)) - { - id = await GetTmdbId(item, cancellationToken).ConfigureAwait(false); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (!string.IsNullOrEmpty(id)) - { - await FetchInfo(person, id, cancellationToken).ConfigureAwait(false); - } - else - { - Logger.Debug("TmdbPersonProvider Unable to obtain id for " + item.Name); - } - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Gets the TMDB id. - /// </summary> - /// <param name="person">The person.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> GetTmdbId(BaseItem person, CancellationToken cancellationToken) - { - string url = string.Format(@"http://api.themoviedb.org/3/search/person?api_key={1}&query={0}", WebUtility.UrlEncode(person.Name), MovieDbProvider.ApiKey); - PersonSearchResults searchResult = null; - - using (Stream json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader - - }).ConfigureAwait(false)) - { - searchResult = JsonSerializer.DeserializeFromStream<PersonSearchResults>(json); - } - - return searchResult != null && searchResult.Total_Results > 0 ? searchResult.Results[0].Id.ToString(UsCulture) : null; - } - - /// <summary> - /// Fetches the info. - /// </summary> - /// <param name="person">The person.</param> - /// <param name="id">The id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchInfo(Person person, string id, CancellationToken cancellationToken) - { - string url = string.Format(@"http://api.themoviedb.org/3/person/{1}?api_key={0}&append_to_response=credits,images", MovieDbProvider.ApiKey, id); - PersonResult searchResult = null; - - using (var json = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - AcceptHeader = MovieDbProvider.AcceptHeader - - }).ConfigureAwait(false)) - { - searchResult = JsonSerializer.DeserializeFromStream<PersonResult>(json); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (searchResult != null) - { - ProcessInfo(person, searchResult); - - //save locally - var memoryStream = new MemoryStream(); - - JsonSerializer.SerializeToStream(searchResult, memoryStream); - - await ProviderManager.SaveToLibraryFilesystem(person, Path.Combine(person.MetaLocation, MetaFileName), memoryStream, cancellationToken); - - Logger.Debug("TmdbPersonProvider downloaded and saved information for {0}", person.Name); - - await FetchImages(person, searchResult.images, cancellationToken).ConfigureAwait(false); - } - } - - /// <summary> - /// Processes the info. - /// </summary> - /// <param name="person">The person.</param> - /// <param name="searchResult">The search result.</param> - protected void ProcessInfo(Person person, PersonResult searchResult) - { - person.Overview = searchResult.biography; - - DateTime date; - - if (DateTime.TryParseExact(searchResult.birthday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date)) - { - person.PremiereDate = date.ToUniversalTime(); - } - - if (DateTime.TryParseExact(searchResult.deathday, "yyyy-MM-dd", new CultureInfo("en-US"), DateTimeStyles.None, out date)) - { - person.EndDate = date.ToUniversalTime(); - } - - if (!string.IsNullOrEmpty(searchResult.homepage)) - { - person.HomePageUrl = searchResult.homepage; - } - - if (!string.IsNullOrEmpty(searchResult.place_of_birth)) - { - person.AddProductionLocation(searchResult.place_of_birth); - } - - person.SetProviderId(MetadataProviders.Tmdb, searchResult.id.ToString(UsCulture)); - } - - /// <summary> - /// Fetches the images. - /// </summary> - /// <param name="person">The person.</param> - /// <param name="searchResult">The search result.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchImages(Person person, Images searchResult, CancellationToken cancellationToken) - { - if (searchResult != null && searchResult.profiles.Count > 0) - { - //get our language - var profile = - searchResult.profiles.FirstOrDefault( - p => - !string.IsNullOrEmpty(GetIso639(p)) && - GetIso639(p).Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, - StringComparison.OrdinalIgnoreCase)); - if (profile == null) - { - //didn't find our language - try first null one - profile = - searchResult.profiles.FirstOrDefault( - p => - !string.IsNullOrEmpty(GetIso639(p)) && - GetIso639(p).Equals(ConfigurationManager.Configuration.PreferredMetadataLanguage, - StringComparison.OrdinalIgnoreCase)); - - } - if (profile == null) - { - //still nothing - just get first one - profile = searchResult.profiles[0]; - } - if (profile != null && !person.HasImage(ImageType.Primary)) - { - var tmdbSettings = await MovieDbProvider.Current.GetTmdbSettings(cancellationToken).ConfigureAwait(false); - - var img = await DownloadAndSaveImage(person, tmdbSettings.images.base_url + ConfigurationManager.Configuration.TmdbFetchedProfileSize + profile.file_path, - "folder" + Path.GetExtension(profile.file_path), cancellationToken).ConfigureAwait(false); - - if (!string.IsNullOrEmpty(img)) - { - person.PrimaryImagePath = img; - } - } - } - } - - private string GetIso639(Profile p) - { - return p.iso_639_1 == null ? string.Empty : p.iso_639_1.ToString(); - } - - /// <summary> - /// Downloads the and save image. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="source">The source.</param> - /// <param name="targetName">Name of the target.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> DownloadAndSaveImage(BaseItem item, string source, string targetName, CancellationToken cancellationToken) - { - if (source == null) return null; - - //download and save locally (if not already there) - var localPath = Path.Combine(item.MetaLocation, targetName); - - using (var sourceStream = await MovieDbProvider.Current.GetMovieDbResponse(new HttpRequestOptions - { - Url = source, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - await ProviderManager.SaveToLibraryFilesystem(item, localPath, sourceStream, cancellationToken).ConfigureAwait(false); - - Logger.Debug("TmdbPersonProvider downloaded and saved image for {0}", item.Name); - } - - return localPath; - } - - #region Result Objects - /// <summary> - /// Class PersonSearchResult - /// </summary> - protected class PersonSearchResult - { - /// <summary> - /// Gets or sets a value indicating whether this <see cref="PersonSearchResult" /> is adult. - /// </summary> - /// <value><c>true</c> if adult; otherwise, <c>false</c>.</value> - public bool Adult { get; set; } - /// <summary> - /// Gets or sets the id. - /// </summary> - /// <value>The id.</value> - public int Id { get; set; } - /// <summary> - /// Gets or sets the name. - /// </summary> - /// <value>The name.</value> - public string Name { get; set; } - /// <summary> - /// Gets or sets the profile_ path. - /// </summary> - /// <value>The profile_ path.</value> - public string Profile_Path { get; set; } - } - - /// <summary> - /// Class PersonSearchResults - /// </summary> - protected class PersonSearchResults - { - /// <summary> - /// Gets or sets the page. - /// </summary> - /// <value>The page.</value> - public int Page { get; set; } - /// <summary> - /// Gets or sets the results. - /// </summary> - /// <value>The results.</value> - public List<PersonSearchResult> Results { get; set; } - /// <summary> - /// Gets or sets the total_ pages. - /// </summary> - /// <value>The total_ pages.</value> - public int Total_Pages { get; set; } - /// <summary> - /// Gets or sets the total_ results. - /// </summary> - /// <value>The total_ results.</value> - public int Total_Results { get; set; } - } - - protected class Cast - { - public int id { get; set; } - public string title { get; set; } - public string character { get; set; } - public string original_title { get; set; } - public string poster_path { get; set; } - public string release_date { get; set; } - public bool adult { get; set; } - } - - protected class Crew - { - public int id { get; set; } - public string title { get; set; } - public string original_title { get; set; } - public string department { get; set; } - public string job { get; set; } - public string poster_path { get; set; } - public string release_date { get; set; } - public bool adult { get; set; } - } - - protected class Credits - { - public List<Cast> cast { get; set; } - public List<Crew> crew { get; set; } - } - - protected class Profile - { - public string file_path { get; set; } - public int width { get; set; } - public int height { get; set; } - public object iso_639_1 { get; set; } - public double aspect_ratio { get; set; } - } - - protected class Images - { - public List<Profile> profiles { get; set; } - } - - protected class PersonResult - { - public bool adult { get; set; } - public List<object> also_known_as { get; set; } - public string biography { get; set; } - public string birthday { get; set; } - public string deathday { get; set; } - public string homepage { get; set; } - public int id { get; set; } - public string imdb_id { get; set; } - public string name { get; set; } - public string place_of_birth { get; set; } - public double popularity { get; set; } - public string profile_path { get; set; } - public Credits credits { get; set; } - public Images images { get; set; } - } - - #endregion - } -} diff --git a/MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs b/MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs deleted file mode 100644 index 67bb35b63..000000000 --- a/MediaBrowser.Controller/Providers/Music/ArtistsPostScanTask.cs +++ /dev/null @@ -1,132 +0,0 @@ -using MediaBrowser.Common.Progress; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class ArtistsPostScanTask - /// </summary> - public class ArtistsPostScanTask : ILibraryPostScanTask - { - /// <summary> - /// The _library manager - /// </summary> - private readonly ILibraryManager _libraryManager; - - /// <summary> - /// Initializes a new instance of the <see cref="ArtistsPostScanTask"/> class. - /// </summary> - /// <param name="libraryManager">The library manager.</param> - public ArtistsPostScanTask(ILibraryManager libraryManager) - { - _libraryManager = libraryManager; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - var allItems = _libraryManager.RootFolder.RecursiveChildren.ToList(); - - var allArtists = await GetAllArtists(allItems).ConfigureAwait(false); - - progress.Report(10); - - var allMusicArtists = allItems.OfType<MusicArtist>().ToList(); - - var numComplete = 0; - - foreach (var artist in allArtists) - { - var musicArtist = FindMusicArtist(artist, allMusicArtists); - - if (musicArtist != null) - { - artist.Images = new Dictionary<ImageType, string>(musicArtist.Images); - - artist.BackdropImagePaths = musicArtist.BackdropImagePaths.ToList(); - artist.ScreenshotImagePaths = musicArtist.ScreenshotImagePaths.ToList(); - artist.SetProviderId(MetadataProviders.Musicbrainz, musicArtist.GetProviderId(MetadataProviders.Musicbrainz)); - } - - numComplete++; - double percent = numComplete; - percent /= allArtists.Length; - percent *= 5; - - progress.Report(10 + percent); - } - - var innerProgress = new ActionableProgress<double>(); - - innerProgress.RegisterAction(pct => progress.Report(15 + pct * .85)); - - await _libraryManager.ValidateArtists(cancellationToken, innerProgress).ConfigureAwait(false); - } - - /// <summary> - /// Gets all artists. - /// </summary> - /// <param name="allItems">All items.</param> - /// <returns>Task{Artist[]}.</returns> - private Task<Artist[]> GetAllArtists(IEnumerable<BaseItem> allItems) - { - var itemsList = allItems.OfType<Audio>().ToList(); - - var tasks = itemsList - .SelectMany(i => - { - var list = new List<string>(); - - if (!string.IsNullOrEmpty(i.AlbumArtist)) - { - list.Add(i.AlbumArtist); - } - if (!string.IsNullOrEmpty(i.Artist)) - { - list.Add(i.Artist); - } - - return list; - }) - .Distinct(StringComparer.OrdinalIgnoreCase) - .Select(i => _libraryManager.GetArtist(i)); - - return Task.WhenAll(tasks); - } - - /// <summary> - /// Finds the music artist. - /// </summary> - /// <param name="artist">The artist.</param> - /// <param name="allMusicArtists">All music artists.</param> - /// <returns>MusicArtist.</returns> - private static MusicArtist FindMusicArtist(Artist artist, IEnumerable<MusicArtist> allMusicArtists) - { - var musicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz); - - return allMusicArtists.FirstOrDefault(i => - { - if (!string.IsNullOrWhiteSpace(musicBrainzId) && string.Equals(musicBrainzId, i.GetProviderId(MetadataProviders.Musicbrainz), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return string.Compare(i.Name, artist.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0; - }); - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs deleted file mode 100644 index 48a114704..000000000 --- a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs +++ /dev/null @@ -1,307 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class FanArtAlbumProvider - /// </summary> - public class FanArtAlbumProvider : FanartBaseProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// The _music brainz resource pool - /// </summary> - private readonly SemaphoreSlim _musicBrainzResourcePool = new SemaphoreSlim(1, 1); - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - internal static FanArtAlbumProvider Current { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="FanArtAlbumProvider"/> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public FanArtAlbumProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - _providerManager = providerManager; - HttpClient = httpClient; - - Current = this; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is MusicAlbum; - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "17"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz))) - { - return false; - } - - if (!ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && - !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) - { - return false; - } - - var comparisonData = Guid.Empty; - - var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz); - - if (!string.IsNullOrEmpty(artistMusicBrainzId)) - { - var artistXmlPath = FanArtArtistProvider.GetArtistDataPath(ConfigurationManager.CommonApplicationPaths, artistMusicBrainzId); - artistXmlPath = Path.Combine(artistXmlPath, "fanart.xml"); - - comparisonData = GetComparisonData(new FileInfo(artistXmlPath)); - } - - // Refresh anytime the parent mbz id changes - if (providerInfo.Data != comparisonData) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <returns>Guid.</returns> - private Guid GetComparisonData(FileInfo artistXmlFileInfo) - { - return artistXmlFileInfo.Exists ? (artistXmlFileInfo.FullName + artistXmlFileInfo.LastWriteTimeUtc.Ticks).GetMD5() : Guid.Empty; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var artistMusicBrainzId = item.Parent.GetProviderId(MetadataProviders.Musicbrainz); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - var comparisonData = Guid.Empty; - - if (!string.IsNullOrEmpty(artistMusicBrainzId)) - { - var artistXmlPath = FanArtArtistProvider.GetArtistDataPath(ConfigurationManager.CommonApplicationPaths, artistMusicBrainzId); - artistXmlPath = Path.Combine(artistXmlPath, "fanart.xml"); - - var artistXmlFileInfo = new FileInfo(artistXmlPath); - - comparisonData = GetComparisonData(artistXmlFileInfo); - - if (artistXmlFileInfo.Exists) - { - var album = (MusicAlbum)item; - - var releaseEntryId = item.GetProviderId(MetadataProviders.Musicbrainz); - - // Fanart uses the release group id so we'll have to get that now using the release entry id - if (string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId)) - { - album.MusicBrainzReleaseGroupId = await GetReleaseGroupId(releaseEntryId, cancellationToken).ConfigureAwait(false); - } - - var doc = new XmlDocument(); - - doc.Load(artistXmlPath); - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.HasImage(ImageType.Disc)) - { - // Try try with the release entry Id, if that doesn't produce anything try the release group id - var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/cdart/@url"); - - if (node == null && !string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId)) - { - node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + album.MusicBrainzReleaseGroupId + "\"]/cdart/@url"); - } - - var path = node != null ? node.Value : null; - - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Disc, await _providerManager.DownloadAndSaveImage(item, path, DiscFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.HasImage(ImageType.Primary)) - { - // Try try with the release entry Id, if that doesn't produce anything try the release group id - var node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + releaseEntryId + "\"]/albumcover/@url"); - - if (node == null && !string.IsNullOrEmpty(album.MusicBrainzReleaseGroupId)) - { - node = doc.SelectSingleNode("//fanart/music/albums/album[@id=\"" + album.MusicBrainzReleaseGroupId + "\"]/albumcover/@url"); - } - - var path = node != null ? node.Value : null; - - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PrimaryFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - } - - } - - data.Data = comparisonData; - SetLastRefreshed(item, DateTime.UtcNow); - - return true; - } - - /// <summary> - /// The _last music brainz request - /// </summary> - private DateTime _lastRequestDate = DateTime.MinValue; - - /// <summary> - /// Gets the music brainz response. - /// </summary> - /// <param name="url">The URL.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{XmlDocument}.</returns> - internal async Task<XmlDocument> GetMusicBrainzResponse(string url, CancellationToken cancellationToken) - { - await _musicBrainzResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - var diff = 1500 - (DateTime.Now - _lastRequestDate).TotalMilliseconds; - - // MusicBrainz is extremely adamant about limiting to one request per second - - if (diff > 0) - { - await Task.Delay(Convert.ToInt32(diff), cancellationToken).ConfigureAwait(false); - } - - _lastRequestDate = DateTime.Now; - - var doc = new XmlDocument(); - - using (var xml = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - CancellationToken = cancellationToken, - UserAgent = Environment.MachineName - - }).ConfigureAwait(false)) - { - using (var oReader = new StreamReader(xml, Encoding.UTF8)) - { - doc.Load(oReader); - } - } - - return doc; - } - finally - { - _lastRequestDate = DateTime.Now; - - _musicBrainzResourcePool.Release(); - } - } - - /// <summary> - /// Gets the release group id internal. - /// </summary> - /// <param name="releaseEntryId">The release entry id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> GetReleaseGroupId(string releaseEntryId, CancellationToken cancellationToken) - { - var url = string.Format("http://www.musicbrainz.org/ws/2/release-group/?query=reid:{0}", releaseEntryId); - - var doc = await GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - var node = doc.SelectSingleNode("//mb:release-group-list/mb:release-group/@id", ns); - - return node != null ? node.Value : null; - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs deleted file mode 100644 index 58200a458..000000000 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistByNameProvider.cs +++ /dev/null @@ -1,48 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class FanArtArtistByNameProvider - /// </summary> - public class FanArtArtistByNameProvider : FanArtArtistProvider - { - /// <summary> - /// Initializes a new instance of the <see cref="FanArtArtistByNameProvider" /> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public FanArtArtistByNameProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(httpClient, logManager, configurationManager, providerManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Artist; - } - - /// <summary> - /// Gets a value indicating whether [save local meta]. - /// </summary> - /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value> - protected override bool SaveLocalMeta - { - get - { - return true; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs deleted file mode 100644 index fc9500b40..000000000 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs +++ /dev/null @@ -1,378 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class FanArtArtistProvider - /// </summary> - public class FanArtArtistProvider : FanartBaseProvider - { - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - internal static FanArtArtistProvider Current; - - /// <summary> - /// Initializes a new instance of the <see cref="FanArtArtistProvider"/> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <exception cref="System.ArgumentNullException">httpClient</exception> - public FanArtArtistProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; - _providerManager = providerManager; - - Current = this; - } - - /// <summary> - /// The fan art base URL - /// </summary> - protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/artist/{0}/{1}/xml/all/1/1"; - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is MusicArtist; - } - - /// <summary> - /// Gets a value indicating whether [save local meta]. - /// </summary> - /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value> - protected virtual bool SaveLocalMeta - { - get { return ConfigurationManager.Configuration.SaveLocalMeta; } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "7"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz))) - { - return false; - } - - if (!ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && - !ConfigurationManager.Configuration.DownloadMusicArtistImages.Backdrops && - !ConfigurationManager.Configuration.DownloadMusicArtistImages.Banner && - !ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo && - !ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary && - - // The fanart album provider depends on xml downloaded here, so honor it's settings too - !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && - !ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary) - { - return false; - } - - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz); - - if (!string.IsNullOrEmpty(musicBrainzId)) - { - // Process images - var path = GetArtistDataPath(ConfigurationManager.ApplicationPaths, musicBrainzId); - - var files = new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.FullName + i.LastWriteTimeUtc.Ticks) - .ToArray(); - - if (files.Length > 0) - { - return string.Join(string.Empty, files).GetMD5(); - } - } - - return Guid.Empty; - } - - /// <summary> - /// The us culture - /// </summary> - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="musicBrainzArtistId">The music brainz artist id.</param> - /// <returns>System.String.</returns> - internal static string GetArtistDataPath(IApplicationPaths appPaths, string musicBrainzArtistId) - { - var seriesDataPath = Path.Combine(GetArtistDataPath(appPaths), musicBrainzArtistId); - - if (!Directory.Exists(seriesDataPath)) - { - Directory.CreateDirectory(seriesDataPath); - } - - return seriesDataPath; - } - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <returns>System.String.</returns> - internal static string GetArtistDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.DataPath, "fanart-music"); - - if (!Directory.Exists(dataPath)) - { - Directory.CreateDirectory(dataPath); - } - - return dataPath; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var musicBrainzId = item.GetProviderId(MetadataProviders.Musicbrainz); - - var artistDataPath = GetArtistDataPath(ConfigurationManager.ApplicationPaths, musicBrainzId); - var xmlPath = Path.Combine(artistDataPath, "fanart.xml"); - - // Only download the xml if it doesn't already exist. The prescan task will take care of getting updates - if (!File.Exists(xmlPath)) - { - await DownloadArtistXml(artistDataPath, musicBrainzId, cancellationToken).ConfigureAwait(false); - } - - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art || - ConfigurationManager.Configuration.DownloadMusicArtistImages.Backdrops || - ConfigurationManager.Configuration.DownloadMusicArtistImages.Banner || - ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo || - ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary) - { - if (File.Exists(xmlPath)) - { - await FetchFromXml(item, xmlPath, cancellationToken).ConfigureAwait(false); - } - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(item); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Downloads the artist XML. - /// </summary> - /// <param name="artistPath">The artist path.</param> - /// <param name="musicBrainzId">The music brainz id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - internal async Task DownloadArtistXml(string artistPath, string musicBrainzId, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var url = string.Format(FanArtBaseUrl, ApiKey, musicBrainzId); - - var xmlPath = Path.Combine(artistPath, "fanart.xml"); - - using (var response = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } - } - } - - /// <summary> - /// Fetches from XML. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="xmlFilePath">The XML file path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchFromXml(BaseItem item, string xmlFilePath, CancellationToken cancellationToken) - { - var doc = new XmlDocument(); - doc.Load(xmlFilePath); - - cancellationToken.ThrowIfCancellationRequested(); - - string path; - var hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hd" : ""; - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Logo && !item.HasImage(ImageType.Logo)) - { - var node = - doc.SelectSingleNode("//fanart/music/musiclogos/" + hd + "musiclogo/@url") ?? - doc.SelectSingleNode("//fanart/music/musiclogos/musiclogo/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Backdrops && item.BackdropImagePaths.Count == 0) - { - var nodes = doc.SelectNodes("//fanart/music/artistbackgrounds//@url"); - if (nodes != null) - { - var numBackdrops = 0; - item.BackdropImagePaths = new List<string>(); - foreach (XmlNode node in nodes) - { - path = node.Value; - if (!string.IsNullOrEmpty(path)) - { - item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("Backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - numBackdrops++; - if (numBackdrops >= ConfigurationManager.Configuration.MaxBackdrops) break; - } - } - - } - - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && !item.HasImage(ImageType.Art)) - { - var node = - doc.SelectSingleNode("//fanart/music/musicarts/" + hd + "musicart/@url") ?? - doc.SelectSingleNode("//fanart/music/musicarts/musicart/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Banner && !item.HasImage(ImageType.Banner)) - { - var node = doc.SelectSingleNode("//fanart/music/musicbanners/" + hd + "musicbanner/@url") ?? - doc.SelectSingleNode("//fanart/music/musicbanners/musicbanner/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - // Artist thumbs are actually primary images (they are square/portrait) - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary && !item.HasImage(ImageType.Primary)) - { - var node = doc.SelectSingleNode("//fanart/music/artistthumbs/artistthumb/@url"); - path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Primary, await _providerManager.DownloadAndSaveImage(item, path, PrimaryFile, SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/FanArtUpdatesPrescanTask.cs b/MediaBrowser.Controller/Providers/Music/FanArtUpdatesPrescanTask.cs deleted file mode 100644 index 8c15bd18a..000000000 --- a/MediaBrowser.Controller/Providers/Music/FanArtUpdatesPrescanTask.cs +++ /dev/null @@ -1,206 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - class FanArtUpdatesPrescanTask : ILibraryPrescanTask - { - private const string UpdatesUrl = "http://api.fanart.tv/webservice/newmusic/{0}/{1}/"; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - private readonly IJsonSerializer _jsonSerializer; - - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public FanArtUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) - { - _jsonSerializer = jsonSerializer; - _config = config; - _logger = logger; - _httpClient = httpClient; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - - var path = FanArtArtistProvider.GetArtistDataPath(_config.CommonApplicationPaths); - - var timestampFile = Path.Combine(path, "time.txt"); - - var timestampFileInfo = new FileInfo(timestampFile); - - // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) - { - return; - } - - // Find out the last time we queried for updates - var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty; - - var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList(); - - // If this is our first time, don't do any updates and just record the timestamp - if (!string.IsNullOrEmpty(lastUpdateTime)) - { - var artistsToUpdate = await GetArtistIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false); - - progress.Report(5); - - await UpdateArtists(artistsToUpdate, path, progress, cancellationToken).ConfigureAwait(false); - } - - var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture); - - File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8); - - progress.Report(100); - } - - /// <summary> - /// Gets the artist ids to update. - /// </summary> - /// <param name="existingArtistIds">The existing series ids.</param> - /// <param name="lastUpdateTime">The last update time.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{IEnumerable{System.String}}.</returns> - private async Task<IEnumerable<string>> GetArtistIdsToUpdate(IEnumerable<string> existingArtistIds, string lastUpdateTime, CancellationToken cancellationToken) - { - // First get last time - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = string.Format(UpdatesUrl, FanartBaseProvider.ApiKey, lastUpdateTime), - CancellationToken = cancellationToken, - EnableHttpCompression = true, - ResourcePool = FanartBaseProvider.FanArtResourcePool - - }).ConfigureAwait(false)) - { - // If empty fanart will return a string of "null", rather than an empty list - using (var reader = new StreamReader(stream)) - { - var json = await reader.ReadToEndAsync().ConfigureAwait(false); - - if (string.Equals(json, "null", StringComparison.OrdinalIgnoreCase)) - { - return new List<string>(); - } - - var updates = _jsonSerializer.DeserializeFromString<List<FanArtUpdate>>(json); - - return updates.Select(i => i.id).Where(i => existingArtistIds.Contains(i, StringComparer.OrdinalIgnoreCase)); - } - } - } - - /// <summary> - /// Updates the artists. - /// </summary> - /// <param name="idList">The id list.</param> - /// <param name="artistsDataPath">The artists data path.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task UpdateArtists(IEnumerable<string> idList, string artistsDataPath, IProgress<double> progress, CancellationToken cancellationToken) - { - var list = idList.ToList(); - var numComplete = 0; - - foreach (var id in list) - { - try - { - await UpdateArtist(id, artistsDataPath, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - // Already logged at lower levels, but don't fail the whole operation, unless something other than a timeout - if (!ex.IsTimedOut) - { - throw; - } - } - - numComplete++; - double percent = numComplete; - percent /= list.Count; - percent *= 95; - - progress.Report(percent + 5); - } - } - - /// <summary> - /// Updates the artist. - /// </summary> - /// <param name="musicBrainzId">The musicBrainzId.</param> - /// <param name="artistsDataPath">The artists data path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private Task UpdateArtist(string musicBrainzId, string artistsDataPath, CancellationToken cancellationToken) - { - _logger.Info("Updating artist " + musicBrainzId); - - artistsDataPath = Path.Combine(artistsDataPath, musicBrainzId); - - if (!Directory.Exists(artistsDataPath)) - { - Directory.CreateDirectory(artistsDataPath); - } - - return FanArtArtistProvider.Current.DownloadArtistXml(artistsDataPath, musicBrainzId, cancellationToken); - } - - /// <summary> - /// Dates the time to unix timestamp. - /// </summary> - /// <param name="dateTime">The date time.</param> - /// <returns>System.Double.</returns> - private static double DateTimeToUnixTimestamp(DateTime dateTime) - { - return (dateTime - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds; - } - - public class FanArtUpdate - { - public string id { get; set; } - public string name { get; set; } - public string new_images { get; set; } - public string total_images { get; set; } - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs deleted file mode 100644 index 77cd2033d..000000000 --- a/MediaBrowser.Controller/Providers/Music/LastfmAlbumProvider.cs +++ /dev/null @@ -1,166 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using MoreLinq; -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - public class LastfmAlbumProvider : LastfmBaseProvider - { - private static readonly Task<string> BlankId = Task.FromResult(""); - - private readonly IProviderManager _providerManager; - - public LastfmAlbumProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(jsonSerializer, httpClient, logManager, configurationManager) - { - _providerManager = providerManager; - LocalMetaFileName = LastfmHelper.LocalAlbumMetaFileName; - } - - protected override Task<string> FindId(BaseItem item, CancellationToken cancellationToken) - { - // We don't fetch by id - return BlankId; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - // If song metadata has changed and we don't have an mbid, refresh - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Musicbrainz)) && - GetComparisonData(item as MusicAlbum) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - protected override async Task FetchLastfmData(BaseItem item, string id, CancellationToken cancellationToken) - { - var result = await GetAlbumResult(item, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - LastfmHelper.ProcessAlbumData(item, result.album); - //And save locally if indicated - if (ConfigurationManager.Configuration.SaveLocalMeta) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(result.album, ms); - - cancellationToken.ThrowIfCancellationRequested(); - - await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - - } - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(item as MusicAlbum); - } - - private async Task<LastfmGetAlbumResult> GetAlbumResult(BaseItem item, CancellationToken cancellationToken) - { - var folder = (Folder)item; - - // Get each song, distinct by the combination of AlbumArtist and Album - var songs = folder.RecursiveChildren.OfType<Audio>().DistinctBy(i => (i.AlbumArtist ?? string.Empty) + (i.Album ?? string.Empty), StringComparer.OrdinalIgnoreCase).ToList(); - - foreach (var song in songs.Where(song => !string.IsNullOrEmpty(song.Album) && !string.IsNullOrEmpty(song.AlbumArtist))) - { - var result = await GetAlbumResult(song.AlbumArtist, song.Album, cancellationToken).ConfigureAwait(false); - - if (result != null && result.album != null) - { - return result; - } - } - - // Try the folder name - return await GetAlbumResult(item.Parent.Name, item.Name, cancellationToken); - } - - private async Task<LastfmGetAlbumResult> GetAlbumResult(string artist, string album, CancellationToken cancellationToken) - { - // Get albu info using artist and album name - var url = RootUrl + string.Format("method=album.getInfo&artist={0}&album={1}&api_key={2}&format=json", UrlEncode(artist), UrlEncode(album), ApiKey); - - using (var json = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - return JsonSerializer.DeserializeFromStream<LastfmGetAlbumResult>(json); - } - } - - protected override Task FetchData(BaseItem item, CancellationToken cancellationToken) - { - return FetchLastfmData(item, string.Empty, cancellationToken); - } - - public override bool Supports(BaseItem item) - { - return item is MusicAlbum; - } - - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the data. - /// </summary> - /// <param name="album">The album.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(MusicAlbum album) - { - var songs = album.RecursiveChildren.OfType<Audio>().ToList(); - - var albumArtists = songs.Select(i => i.AlbumArtist) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - var albumNames = songs.Select(i => i.AlbumArtist) - .Where(i => !string.IsNullOrEmpty(i)) - .Distinct(StringComparer.OrdinalIgnoreCase) - .ToList(); - - albumArtists.AddRange(albumNames); - - return string.Join(string.Empty, albumArtists.OrderBy(i => i).ToArray()).GetMD5(); - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistByNameProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistByNameProvider.cs deleted file mode 100644 index c64a7fc99..000000000 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistByNameProvider.cs +++ /dev/null @@ -1,117 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class LastfmArtistByNameProvider - /// </summary> - public class LastfmArtistByNameProvider : LastfmArtistProvider - { - /// <summary> - /// Initializes a new instance of the <see cref="LastfmArtistByNameProvider" /> class. - /// </summary> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <param name="libraryManager">The library manager.</param> - public LastfmArtistByNameProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, ILibraryManager libraryManager) - : base(jsonSerializer, httpClient, logManager, configurationManager, providerManager, libraryManager) - { - } - - /// <summary> - /// Gets a value indicating whether [save local meta]. - /// </summary> - /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value> - protected override bool SaveLocalMeta - { - get - { - return true; - } - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Artist; - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "7"; - } - } - - /// <summary> - /// Fetches the lastfm data. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="musicBrainzId">The music brainz id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - protected override async Task FetchLastfmData(BaseItem item, string musicBrainzId, CancellationToken cancellationToken) - { - var artist = (Artist)item; - - // See if we can avoid an http request by finding the matching MusicArtist entity - var musicArtist = FindMusicArtist(artist, LibraryManager); - - if (musicArtist != null) - { - LastfmHelper.ProcessArtistData(musicArtist, artist); - } - else - { - await base.FetchLastfmData(item, musicBrainzId, cancellationToken).ConfigureAwait(false); - } - } - - - /// <summary> - /// Finds the music artist. - /// </summary> - /// <param name="artist">The artist.</param> - /// <param name="libraryManager">The library manager.</param> - /// <returns>MusicArtist.</returns> - private static MusicArtist FindMusicArtist(Artist artist, ILibraryManager libraryManager) - { - var musicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz); - - return libraryManager.RootFolder.RecursiveChildren - .OfType<MusicArtist>() - .FirstOrDefault(i => - { - if (!string.IsNullOrWhiteSpace(musicBrainzId) && string.Equals(musicBrainzId, i.GetProviderId(MetadataProviders.Musicbrainz), StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - return false; - }); - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs deleted file mode 100644 index 38475317e..000000000 --- a/MediaBrowser.Controller/Providers/Music/LastfmArtistProvider.cs +++ /dev/null @@ -1,283 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; -using System; -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; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class LastfmArtistProvider - /// </summary> - public class LastfmArtistProvider : LastfmBaseProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - /// <summary> - /// The _library manager - /// </summary> - protected readonly ILibraryManager LibraryManager; - - /// <summary> - /// Initializes a new instance of the <see cref="LastfmArtistProvider"/> class. - /// </summary> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <param name="libraryManager">The library manager.</param> - public LastfmArtistProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, ILibraryManager libraryManager) - : base(jsonSerializer, httpClient, logManager, configurationManager) - { - _providerManager = providerManager; - LibraryManager = libraryManager; - LocalMetaFileName = LastfmHelper.LocalArtistMetaFileName; - } - - /// <summary> - /// Finds the id. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - protected override async Task<string> FindId(BaseItem item, CancellationToken cancellationToken) - { - if (item is Artist) - { - // Since MusicArtists are refreshed first, try to find it from one of them - var id = FindIdFromMusicArtistEntity(item); - - if (!string.IsNullOrEmpty(id)) - { - return id; - } - } - - // Try to find the id using last fm - var result = await FindIdFromLastFm(item, cancellationToken).ConfigureAwait(false); - - if (result != null) - { - if (!string.IsNullOrEmpty(result)) - { - return result; - } - } - - try - { - // If we don't get anything, go directly to music brainz - return await FindIdFromMusicBrainz(item, cancellationToken).ConfigureAwait(false); - } - catch (HttpException e) - { - if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.BadRequest) - { - // They didn't like a character in the name. Handle the exception so that the provider doesn't keep retrying over and over - return null; - } - - throw; - } - } - - /// <summary> - /// Finds the id from music artist entity. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>System.String.</returns> - private string FindIdFromMusicArtistEntity(BaseItem item) - { - var artist = LibraryManager.RootFolder.RecursiveChildren.OfType<MusicArtist>() - .FirstOrDefault(i => string.Compare(i.Name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0); - - return artist != null ? artist.GetProviderId(MetadataProviders.Musicbrainz) : null; - } - - /// <summary> - /// Finds the id from last fm. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> FindIdFromLastFm(BaseItem item, CancellationToken cancellationToken) - { - //Execute the Artist search against our name and assume first one is the one we want - var url = RootUrl + string.Format("method=artist.search&artist={0}&api_key={1}&format=json", UrlEncode(item.Name), ApiKey); - - using (var json = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - using (var reader = new StreamReader(json, true)) - { - var jsonString = await reader.ReadToEndAsync().ConfigureAwait(false); - - // Sometimes they send back an empty response or just the text "null" - if (!jsonString.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - { - return null; - } - - var searchResult = JsonSerializer.DeserializeFromString<LastfmArtistSearchResults>(jsonString); - - if (searchResult != null && searchResult.results != null && searchResult.results.artistmatches != null && searchResult.results.artistmatches.artist.Count > 0) - { - var artist = searchResult.results.artistmatches.artist - .FirstOrDefault(i => i.name != null && string.Compare(i.name, item.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0); - - return artist == null ? null : artist.mbid; - } - } - } - - return null; - } - - /// <summary> - /// Finds the id from music brainz. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> FindIdFromMusicBrainz(BaseItem item, CancellationToken cancellationToken) - { - // They seem to throw bad request failures on any term with a slash - var nameToSearch = item.Name.Replace('/', ' '); - - var url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=artist:{0}", UrlEncode(nameToSearch)); - - var doc = await FanArtAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - var ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - var node = doc.SelectSingleNode("//mb:artist-list/mb:artist[@type='Group']/@id", ns); - - if (node != null && node.Value != null) - { - return node.Value; - } - - if (HasDiacritics(item.Name)) - { - // Try again using the search with accent characters url - url = string.Format("http://www.musicbrainz.org/ws/2/artist/?query=artistaccent:{0}", UrlEncode(nameToSearch)); - - doc = await FanArtAlbumProvider.Current.GetMusicBrainzResponse(url, cancellationToken).ConfigureAwait(false); - - ns = new XmlNamespaceManager(doc.NameTable); - ns.AddNamespace("mb", "http://musicbrainz.org/ns/mmd-2.0#"); - node = doc.SelectSingleNode("//mb:artist-list/mb:artist[@type='Group']/@id", ns); - - if (node != null && node.Value != null) - { - return node.Value; - } - } - - return null; - } - - /// <summary> - /// Determines whether the specified text has diacritics. - /// </summary> - /// <param name="text">The text.</param> - /// <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); - } - - /// <summary> - /// Fetches the lastfm data. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="musicBrainzId">The music brainz id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - protected override async Task FetchLastfmData(BaseItem item, string musicBrainzId, CancellationToken cancellationToken) - { - // Get artist info with provided id - var url = RootUrl + string.Format("method=artist.getInfo&mbid={0}&api_key={1}&format=json", UrlEncode(musicBrainzId), ApiKey); - - LastfmGetArtistResult result; - - using (var json = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = LastfmResourcePool, - CancellationToken = cancellationToken, - EnableHttpCompression = false - - }).ConfigureAwait(false)) - { - result = JsonSerializer.DeserializeFromStream<LastfmGetArtistResult>(json); - } - - if (result != null && result.artist != null) - { - LastfmHelper.ProcessArtistData(item, result.artist); - //And save locally if indicated - if (SaveLocalMeta) - { - var ms = new MemoryStream(); - JsonSerializer.SerializeToStream(result.artist, ms); - - if (cancellationToken.IsCancellationRequested) - { - ms.Dispose(); - cancellationToken.ThrowIfCancellationRequested(); - } - - await _providerManager.SaveToLibraryFilesystem(item, Path.Combine(item.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - - } - } - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is MusicArtist; - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs b/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs deleted file mode 100644 index 54fe32959..000000000 --- a/MediaBrowser.Controller/Providers/Music/LastfmBaseProvider.cs +++ /dev/null @@ -1,277 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.Collections.Generic; -using System.Net; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - /// <summary> - /// Class MovieDbProvider - /// </summary> - public abstract class LastfmBaseProvider : BaseMetadataProvider - { - protected static readonly SemaphoreSlim LastfmResourcePool = new SemaphoreSlim(4, 4); - - /// <summary> - /// Initializes a new instance of the <see cref="LastfmBaseProvider" /> class. - /// </summary> - /// <param name="jsonSerializer">The json serializer.</param> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <exception cref="System.ArgumentNullException">jsonSerializer</exception> - protected LastfmBaseProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - JsonSerializer = jsonSerializer; - HttpClient = httpClient; - } - - protected override string ProviderVersion - { - get - { - return "5"; - } - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// The name of the local json meta file for this item type - /// </summary> - protected string LocalMetaFileName { get; set; } - - protected virtual bool SaveLocalMeta - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - /// <summary> - /// If we save locally, refresh if they delete something - /// </summary> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return SaveLocalMeta; - } - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - protected const string RootUrl = @"http://ws.audioscrobbler.com/2.0/?"; - protected static string ApiKey = "7b76553c3eb1d341d642755aecc40a33"; - - /// <summary> - /// Determines whether [has local meta] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if [has local meta] [the specified item]; otherwise, <c>false</c>.</returns> - protected bool HasLocalMeta(BaseItem item) - { - return item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName); - } - - /// <summary> - /// Fetches the items data. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken"></param> - /// <returns>Task.</returns> - protected virtual async Task FetchData(BaseItem item, CancellationToken cancellationToken) - { - var id = item.GetProviderId(MetadataProviders.Musicbrainz) ?? await FindId(item, cancellationToken).ConfigureAwait(false); - if (!string.IsNullOrWhiteSpace(id)) - { - Logger.Debug("LastfmProvider - getting info for {0}", item.Name); - - cancellationToken.ThrowIfCancellationRequested(); - - item.SetProviderId(MetadataProviders.Musicbrainz, id); - - await FetchLastfmData(item, id, cancellationToken).ConfigureAwait(false); - } - else - { - Logger.Info("LastfmProvider could not find " + item.Name + ". Check name on Last.fm."); - } - - } - - protected abstract Task<string> FindId(BaseItem item, CancellationToken cancellationToken); - - protected abstract Task FetchLastfmData(BaseItem item, string id, CancellationToken cancellationToken); - - /// <summary> - /// Encodes an URL. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>System.String.</returns> - protected static string UrlEncode(string name) - { - return WebUtility.UrlEncode(name); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - await FetchData(item, cancellationToken).ConfigureAwait(false); - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - } - - #region Result Objects - - public class LastfmStats - { - public string listeners { get; set; } - public string playcount { get; set; } - } - - public class LastfmTag - { - public string name { get; set; } - public string url { get; set; } - } - - - public class LastfmTags - { - public List<LastfmTag> tag { get; set; } - } - - public class LastfmFormationInfo - { - public string yearfrom { get; set; } - public string yearto { get; set; } - } - - public class LastFmBio - { - public string published { get; set; } - public string summary { get; set; } - public string content { get; set; } - public string placeformed { get; set; } - public string yearformed { get; set; } - public List<LastfmFormationInfo> formationlist { get; set; } - } - - public class LastfmArtist - { - public string name { get; set; } - public string mbid { get; set; } - public string url { get; set; } - public string streamable { get; set; } - public string ontour { get; set; } - public LastfmStats stats { get; set; } - public List<LastfmArtist> similar { get; set; } - public LastfmTags tags { get; set; } - public LastFmBio bio { get; set; } - } - - - public class LastfmAlbum - { - public string name { get; set; } - public string artist { get; set; } - public string id { get; set; } - public string mbid { get; set; } - public string releasedate { get; set; } - public int listeners { get; set; } - public int playcount { get; set; } - public LastfmTags toptags { get; set; } - public LastFmBio wiki { get; set; } - } - - public class LastfmGetAlbumResult - { - public LastfmAlbum album { get; set; } - } - - public class LastfmGetArtistResult - { - public LastfmArtist artist { get; set; } - } - - public class Artistmatches - { - public List<LastfmArtist> artist { get; set; } - } - - public class LastfmArtistSearchResult - { - public Artistmatches artistmatches { get; set; } - } - - public class LastfmArtistSearchResults - { - public LastfmArtistSearchResult results { get; set; } - } - - #endregion -} diff --git a/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs b/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs deleted file mode 100644 index ca9f8bc71..000000000 --- a/MediaBrowser.Controller/Providers/Music/LastfmHelper.cs +++ /dev/null @@ -1,73 +0,0 @@ -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using System; -using System.Linq; - -namespace MediaBrowser.Controller.Providers.Music -{ - public static class LastfmHelper - { - public static string LocalArtistMetaFileName = "lastfmartist.json"; - public static string LocalAlbumMetaFileName = "lastfmalbum.json"; - - public static void ProcessArtistData(BaseItem artist, LastfmArtist data) - { - var yearFormed = 0; - - if (data.bio != null) - { - Int32.TryParse(data.bio.yearformed, out yearFormed); - - artist.Overview = data.bio.content; - - if (!string.IsNullOrEmpty(data.bio.placeformed)) - { - artist.AddProductionLocation(data.bio.placeformed); - } - } - - artist.PremiereDate = yearFormed > 0 ? new DateTime(yearFormed, 1, 1, 0, 0, 0, DateTimeKind.Utc) : (DateTime?)null; - artist.ProductionYear = yearFormed; - if (data.tags != null) - { - AddTags(artist, data.tags); - } - } - - public static void ProcessArtistData(MusicArtist source, Artist target) - { - target.PremiereDate = source.PremiereDate; - target.ProductionYear = source.ProductionYear; - target.Tags = source.Tags.ToList(); - target.Overview = source.Overview; - target.ProductionLocations = source.ProductionLocations.ToList(); - target.Genres = source.Genres.ToList(); - } - - public static void ProcessAlbumData(BaseItem item, LastfmAlbum data) - { - if (!string.IsNullOrWhiteSpace(data.mbid)) item.SetProviderId(MetadataProviders.Musicbrainz, data.mbid); - - var overview = data.wiki != null ? data.wiki.content : null; - - item.Overview = overview; - - DateTime release; - DateTime.TryParse(data.releasedate, out release); - item.PremiereDate = release; - item.ProductionYear = release.Year; - if (data.toptags != null) - { - AddTags(item, data.toptags); - } - } - - private static void AddTags(BaseItem item, LastfmTags tags) - { - var itemTags = (from tag in tags.tag where !string.IsNullOrEmpty(tag.name) select tag.name).ToList(); - - item.Tags = itemTags; - } - } -} diff --git a/MediaBrowser.Controller/Providers/Music/MusicArtistProviderFromJson.cs b/MediaBrowser.Controller/Providers/Music/MusicArtistProviderFromJson.cs deleted file mode 100644 index e3df46ed2..000000000 --- a/MediaBrowser.Controller/Providers/Music/MusicArtistProviderFromJson.cs +++ /dev/null @@ -1,100 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.Audio; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Serialization; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Controller.Providers.Music -{ - public class MusicArtistProviderFromJson : BaseMetadataProvider - { - /// <summary> - /// Gets the json serializer. - /// </summary> - /// <value>The json serializer.</value> - protected IJsonSerializer JsonSerializer { get; private set; } - - public MusicArtistProviderFromJson(IJsonSerializer jsonSerializer, ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - if (jsonSerializer == null) - { - throw new ArgumentNullException("jsonSerializer"); - } - JsonSerializer = jsonSerializer; - - } - - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LastfmHelper.LocalArtistMetaFileName)); - if (entry != null) - { - // read in our saved meta and pass to processing function - var data = JsonSerializer.DeserializeFromFile<LastfmArtist>(entry.FullName); - - cancellationToken.ThrowIfCancellationRequested(); - - LastfmHelper.ProcessArtistData(item, data); - - item.SetProviderId(MetadataProviders.Musicbrainz, data.mbid); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - return FalseTaskResult; - } - - public override MetadataProviderPriority Priority - { - get - { - return MetadataProviderPriority.First; - } - } - - public override bool Supports(BaseItem item) - { - return false; - } - - public override bool RequiresInternet - { - get - { - return false; - } - } - - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (!item.ResolveArgs.ContainsMetaFileByName(LastfmHelper.LocalArtistMetaFileName)) - { - return false; // nothing to read - } - - // Need to re-override to jump over intermediate implementation - return CompareDate(item) > providerInfo.LastRefreshed; - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, LastfmHelper.LocalArtistMetaFileName)); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - } -} diff --git a/MediaBrowser.Controller/Providers/TV/EpisodeImageFromMediaLocationProvider.cs b/MediaBrowser.Controller/Providers/TV/EpisodeImageFromMediaLocationProvider.cs deleted file mode 100644 index 7eaf95a08..000000000 --- a/MediaBrowser.Controller/Providers/TV/EpisodeImageFromMediaLocationProvider.cs +++ /dev/null @@ -1,132 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class EpisodeImageFromMediaLocationProvider - /// </summary> - public class EpisodeImageFromMediaLocationProvider : BaseMetadataProvider - { - public EpisodeImageFromMediaLocationProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Episode && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var episode = (Episode)item; - - var episodeFileName = Path.GetFileName(episode.Path); - - var parent = item.ResolveArgs.Parent; - - ValidateImage(episode, item.MetaLocation); - - cancellationToken.ThrowIfCancellationRequested(); - - SetPrimaryImagePath(episode, parent, item.MetaLocation, episodeFileName); - - SetLastRefreshed(item, DateTime.UtcNow); - return TrueTaskResult; - } - - /// <summary> - /// Validates the primary image path still exists - /// </summary> - /// <param name="episode">The episode.</param> - /// <param name="metadataFolderPath">The metadata folder path.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private void ValidateImage(Episode episode, string metadataFolderPath) - { - var path = episode.PrimaryImagePath; - - if (string.IsNullOrEmpty(path)) - { - return; - } - - // Only validate images in the season/metadata folder - if (!string.Equals(Path.GetDirectoryName(path), metadataFolderPath, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (episode.Parent.ResolveArgs.GetMetaFileByPath(path) == null) - { - episode.PrimaryImagePath = null; - } - } - - /// <summary> - /// Sets the primary image path. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="parent">The parent.</param> - /// <param name="metadataFolder">The metadata folder.</param> - /// <param name="episodeFileName">Name of the episode file.</param> - private void SetPrimaryImagePath(Episode item, Folder parent, string metadataFolder, string episodeFileName) - { - // Look for the image file in the metadata folder, and if found, set PrimaryImagePath - var imageFiles = new[] { - Path.Combine(metadataFolder, Path.ChangeExtension(episodeFileName, ".jpg")), - Path.Combine(metadataFolder, Path.ChangeExtension(episodeFileName, ".png")) - }; - - var file = parent.ResolveArgs.GetMetaFileByPath(imageFiles[0]) ?? - parent.ResolveArgs.GetMetaFileByPath(imageFiles[1]); - - if (file != null) - { - item.PrimaryImagePath = file.FullName; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs b/MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs deleted file mode 100644 index 0e7f1e66e..000000000 --- a/MediaBrowser.Controller/Providers/TV/EpisodeProviderFromXml.cs +++ /dev/null @@ -1,116 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class EpisodeProviderFromXml - /// </summary> - public class EpisodeProviderFromXml : BaseMetadataProvider - { - public EpisodeProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Episode && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - return Fetch(item, cancellationToken); - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var metadataFile = Path.Combine(item.MetaLocation, Path.ChangeExtension(Path.GetFileName(item.Path), ".xml")); - - var file = item.ResolveArgs.Parent.ResolveArgs.GetMetaFileByPath(metadataFile); - - if (file == null) - { - return base.CompareDate(item); - } - - return file.LastWriteTimeUtc; - } - - /// <summary> - /// Fetches the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = Path.Combine(item.MetaLocation, Path.ChangeExtension(Path.GetFileName(item.Path), ".xml")); - - var file = item.ResolveArgs.Parent.ResolveArgs.GetMetaFileByPath(metadataFile); - - if (file == null) - { - return false; - } - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - new EpisodeXmlParser(Logger).Fetch((Episode)item, metadataFile, cancellationToken); - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/EpisodeXmlParser.cs b/MediaBrowser.Controller/Providers/TV/EpisodeXmlParser.cs deleted file mode 100644 index 7ace1047e..000000000 --- a/MediaBrowser.Controller/Providers/TV/EpisodeXmlParser.cs +++ /dev/null @@ -1,119 +0,0 @@ -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Logging; -using System.IO; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class EpisodeXmlParser - /// </summary> - public class EpisodeXmlParser : BaseItemXmlParser<Episode> - { - /// <summary> - /// Initializes a new instance of the <see cref="EpisodeXmlParser" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - public EpisodeXmlParser(ILogger logger) - : base(logger) - { - } - - /// <summary> - /// Fetches the data from XML node. - /// </summary> - /// <param name="reader">The reader.</param> - /// <param name="item">The item.</param> - protected override void FetchDataFromXmlNode(XmlReader reader, Episode item) - { - switch (reader.Name) - { - case "Episode": - //MB generated metadata is within an "Episode" node - using (var subTree = reader.ReadSubtree()) - { - subTree.MoveToContent(); - - // Loop through each element - while (subTree.Read()) - { - if (subTree.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(subTree, item); - } - } - - } - break; - - case "filename": - { - var filename = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(filename)) - { - // Strip off everything but the filename. Some metadata tools like MetaBrowser v1.0 will have an 'episodes' prefix - // even though it's actually using the metadata folder. - filename = Path.GetFileName(filename); - - var seasonFolder = Path.GetDirectoryName(item.Path); - filename = Path.Combine(seasonFolder, "metadata", filename); - - if (File.Exists(filename)) - { - item.PrimaryImagePath = filename; - } - } - break; - } - case "SeasonNumber": - { - var number = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(number)) - { - int num; - - if (int.TryParse(number, out num)) - { - item.ParentIndexNumber = num; - } - } - break; - } - - case "EpisodeNumber": - { - var number = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(number)) - { - int num; - - if (int.TryParse(number, out num)) - { - item.IndexNumber = num; - } - } - break; - } - - case "EpisodeName": - { - var name = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(name)) - { - item.Name = name; - } - break; - } - - - default: - base.FetchDataFromXmlNode(reader, item); - break; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs deleted file mode 100644 index 167f855a4..000000000 --- a/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs +++ /dev/null @@ -1,229 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class FanArtSeasonProvider - /// </summary> - class FanArtSeasonProvider : FanartBaseProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Initializes a new instance of the <see cref="FanArtSeasonProvider"/> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public FanArtSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - _providerManager = providerManager; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Season; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var season = (Season)item; - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(FanArtTvProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "fanart.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - return GetComparisonData(imagesFileInfo); - } - - return Guid.Empty; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="imagesFileInfo">The images file info.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(FileInfo imagesFileInfo) - { - var date = imagesFileInfo.Exists ? imagesFileInfo.LastWriteTimeUtc : DateTime.MinValue; - - var key = date.Ticks + imagesFileInfo.FullName; - - return key.GetMD5(); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var season = (Season)item; - - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(FanArtTvProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "fanart.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - if (imagesFileInfo.Exists) - { - if (!season.HasImage(ImageType.Thumb)) - { - var xmlDoc = new XmlDocument(); - xmlDoc.Load(imagesXmlPath); - - await FetchImages(season, xmlDoc, cancellationToken).ConfigureAwait(false); - } - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(imagesFileInfo); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - return false; - } - - /// <summary> - /// Fetches the images. - /// </summary> - /// <param name="season">The season.</param> - /// <param name="doc">The doc.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchImages(Season season, XmlDocument doc, CancellationToken cancellationToken) - { - var seasonNumber = season.IndexNumber ?? -1; - - if (seasonNumber == -1) - { - return; - } - - var language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower(); - - if (ConfigurationManager.Configuration.DownloadSeasonImages.Thumb && !season.HasImage(ImageType.Thumb)) - { - var node = doc.SelectSingleNode("//fanart/series/seasonthumbs/seasonthumb[@lang = \"" + language + "\"][@season = \"" + seasonNumber + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/seasonthumbs/seasonthumb[@season = \"" + seasonNumber + "\"]/@url"); - - var path = node != null ? node.Value : null; - - if (!string.IsNullOrEmpty(path)) - { - season.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(season, path, ThumbFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "3"; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs deleted file mode 100644 index 164c5d0c9..000000000 --- a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs +++ /dev/null @@ -1,338 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.IO; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - class FanArtTvProvider : FanartBaseProvider - { - protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/series/{0}/{1}/xml/all/1/1"; - - internal static FanArtTvProvider Current { get; private set; } - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - private readonly IProviderManager _providerManager; - - public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; - _providerManager = providerManager; - Current = this; - } - - public override bool Supports(BaseItem item) - { - return item is Series; - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tvdb))) - { - return false; - } - - if (!ConfigurationManager.Configuration.DownloadSeriesImages.Art && - !ConfigurationManager.Configuration.DownloadSeriesImages.Logo && - !ConfigurationManager.Configuration.DownloadSeriesImages.Thumb && - !ConfigurationManager.Configuration.DownloadSeriesImages.Backdrops && - !ConfigurationManager.Configuration.DownloadSeriesImages.Banner) - { - return false; - } - - if (item.HasImage(ImageType.Art) && - item.HasImage(ImageType.Logo) && - item.HasImage(ImageType.Banner) && - item.HasImage(ImageType.Thumb) && - item.BackdropImagePaths.Count > 0) - { - return false; - } - - if (providerInfo.Data != GetComparisonData(item.GetProviderId(MetadataProviders.Tvdb))) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <returns>Guid.</returns> - private Guid GetComparisonData(string id) - { - if (!string.IsNullOrEmpty(id)) - { - // Process images - var path = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, id); - - var files = new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.FullName + i.LastWriteTimeUtc.Ticks) - .ToArray(); - - if (files.Length > 0) - { - return string.Join(string.Empty, files).GetMD5(); - } - } - - return Guid.Empty; - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="seriesId">The series id.</param> - /// <returns>System.String.</returns> - internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId) - { - var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); - - if (!Directory.Exists(seriesDataPath)) - { - Directory.CreateDirectory(seriesDataPath); - } - - return seriesDataPath; - } - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <returns>System.String.</returns> - internal static string GetSeriesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.DataPath, "fanart-tv"); - - if (!Directory.Exists(dataPath)) - { - Directory.CreateDirectory(dataPath); - } - - return dataPath; - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - BaseProviderInfo data; - - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - var seriesId = item.GetProviderId(MetadataProviders.Tvdb); - - var seriesDataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId); - var xmlPath = Path.Combine(seriesDataPath, "fanart.xml"); - - // Only download the xml if it doesn't already exist. The prescan task will take care of getting updates - if (!File.Exists(xmlPath)) - { - await DownloadSeriesXml(seriesDataPath, seriesId, cancellationToken).ConfigureAwait(false); - } - - if (File.Exists(xmlPath)) - { - await FetchFromXml(item, xmlPath, cancellationToken).ConfigureAwait(false); - } - - data.Data = GetComparisonData(item.GetProviderId(MetadataProviders.Tvdb)); - SetLastRefreshed(item, DateTime.UtcNow); - - return true; - } - - /// <summary> - /// Fetches from XML. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="xmlFilePath">The XML file path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchFromXml(BaseItem item, string xmlFilePath, CancellationToken cancellationToken) - { - var doc = new XmlDocument(); - doc.Load(xmlFilePath); - - cancellationToken.ThrowIfCancellationRequested(); - - var language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower(); - - var hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hdtv" : "clear"; - if (ConfigurationManager.Configuration.DownloadSeriesImages.Logo && !item.HasImage(ImageType.Logo)) - { - var node = doc.SelectSingleNode("//fanart/series/" + hd + "logos/" + hd + "logo[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/clearlogos/clearlogo[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/" + hd + "logos/" + hd + "logo/@url") ?? - doc.SelectSingleNode("//fanart/series/clearlogos/clearlogo/@url"); - var path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(item, path, LogoFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - hd = ConfigurationManager.Configuration.DownloadHDFanArt ? "hd" : ""; - if (ConfigurationManager.Configuration.DownloadSeriesImages.Art && !item.HasImage(ImageType.Art)) - { - var node = doc.SelectSingleNode("//fanart/series/" + hd + "cleararts/" + hd + "clearart[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/cleararts/clearart[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/" + hd + "cleararts/" + hd + "clearart/@url") ?? - doc.SelectSingleNode("//fanart/series/cleararts/clearart/@url"); - var path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(item, path, ArtFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (ConfigurationManager.Configuration.DownloadSeriesImages.Thumb && !item.HasImage(ImageType.Thumb)) - { - var node = doc.SelectSingleNode("//fanart/series/tvthumbs/tvthumb[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/tvthumbs/tvthumb/@url"); - var path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(item, path, ThumbFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - if (ConfigurationManager.Configuration.DownloadSeriesImages.Banner && !item.HasImage(ImageType.Banner)) - { - var node = doc.SelectSingleNode("//fanart/series/tbbanners/tvbanner[@lang = \"" + language + "\"]/@url") ?? - doc.SelectSingleNode("//fanart/series/tbbanners/tvbanner/@url"); - var path = node != null ? node.Value : null; - if (!string.IsNullOrEmpty(path)) - { - item.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(item, path, BannerFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - - if (ConfigurationManager.Configuration.DownloadMovieImages.Backdrops && item.BackdropImagePaths.Count == 0) - { - var nodes = doc.SelectNodes("//fanart/series/showbackgrounds//@url"); - - if (nodes != null) - { - var numBackdrops = item.BackdropImagePaths.Count; - - foreach (XmlNode node in nodes) - { - var path = node.Value; - - if (!string.IsNullOrEmpty(path)) - { - item.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(item, path, ("backdrop" + (numBackdrops > 0 ? numBackdrops.ToString(UsCulture) : "") + ".jpg"), ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); - - numBackdrops++; - - if (item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) break; - } - } - - } - } - - } - - /// <summary> - /// Downloads the series XML. - /// </summary> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="tvdbId">The TVDB id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - internal async Task DownloadSeriesXml(string seriesDataPath, string tvdbId, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - string url = string.Format(FanArtBaseUrl, ApiKey, tvdbId); - - var xmlPath = Path.Combine(seriesDataPath, "fanart.xml"); - - using (var response = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) - { - await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); - } - } - } - - } -} diff --git a/MediaBrowser.Controller/Providers/TV/FanArtTvUpdatesPrescanTask.cs b/MediaBrowser.Controller/Providers/TV/FanArtTvUpdatesPrescanTask.cs deleted file mode 100644 index 84de5140e..000000000 --- a/MediaBrowser.Controller/Providers/TV/FanArtTvUpdatesPrescanTask.cs +++ /dev/null @@ -1,200 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers.Music; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Serialization; - -namespace MediaBrowser.Controller.Providers.TV -{ - class FanArtTvUpdatesPrescanTask : ILibraryPrescanTask - { - private const string UpdatesUrl = "http://api.fanart.tv/webservice/newtv/{0}/{1}/"; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - private readonly IJsonSerializer _jsonSerializer; - - private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - public FanArtTvUpdatesPrescanTask(IJsonSerializer jsonSerializer, IServerConfigurationManager config, ILogger logger, IHttpClient httpClient) - { - _jsonSerializer = jsonSerializer; - _config = config; - _logger = logger; - _httpClient = httpClient; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - - var path = FanArtTvProvider.GetSeriesDataPath(_config.CommonApplicationPaths); - - var timestampFile = Path.Combine(path, "time.txt"); - - var timestampFileInfo = new FileInfo(timestampFile); - - // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) - { - return; - } - - // Find out the last time we queried for updates - var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty; - - var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList(); - - // If this is our first time, don't do any updates and just record the timestamp - if (!string.IsNullOrEmpty(lastUpdateTime)) - { - var seriesToUpdate = await GetSeriesIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false); - - progress.Report(5); - - await UpdateSeries(seriesToUpdate, path, progress, cancellationToken).ConfigureAwait(false); - } - - var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture); - - File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8); - - progress.Report(100); - } - - /// <summary> - /// Gets the series ids to update. - /// </summary> - /// <param name="existingSeriesIds">The existing series ids.</param> - /// <param name="lastUpdateTime">The last update time.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{IEnumerable{System.String}}.</returns> - private async Task<IEnumerable<string>> GetSeriesIdsToUpdate(IEnumerable<string> existingSeriesIds, string lastUpdateTime, CancellationToken cancellationToken) - { - // First get last time - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = string.Format(UpdatesUrl, FanartBaseProvider.ApiKey, lastUpdateTime), - CancellationToken = cancellationToken, - EnableHttpCompression = true, - ResourcePool = FanartBaseProvider.FanArtResourcePool - - }).ConfigureAwait(false)) - { - // If empty fanart will return a string of "null", rather than an empty list - using (var reader = new StreamReader(stream)) - { - var json = await reader.ReadToEndAsync().ConfigureAwait(false); - - if (string.Equals(json, "null", StringComparison.OrdinalIgnoreCase)) - { - return new List<string>(); - } - - var updates = _jsonSerializer.DeserializeFromString<List<FanArtUpdatesPrescanTask.FanArtUpdate>>(json); - - return updates.Select(i => i.id).Where(i => existingSeriesIds.Contains(i, StringComparer.OrdinalIgnoreCase)); - } - } - } - - /// <summary> - /// Updates the series. - /// </summary> - /// <param name="idList">The id list.</param> - /// <param name="seriesDataPath">The artists data path.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task UpdateSeries(IEnumerable<string> idList, string seriesDataPath, IProgress<double> progress, CancellationToken cancellationToken) - { - var list = idList.ToList(); - var numComplete = 0; - - foreach (var id in list) - { - try - { - await UpdateSeries(id, seriesDataPath, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - // Already logged at lower levels, but don't fail the whole operation, unless something other than a timeout - if (!ex.IsTimedOut) - { - throw; - } - } - - numComplete++; - double percent = numComplete; - percent /= list.Count; - percent *= 95; - - progress.Report(percent + 5); - } - } - - private Task UpdateSeries(string tvdbId, string seriesDataPath, CancellationToken cancellationToken) - { - _logger.Info("Updating series " + tvdbId); - - seriesDataPath = Path.Combine(seriesDataPath, tvdbId); - - if (!Directory.Exists(seriesDataPath)) - { - Directory.CreateDirectory(seriesDataPath); - } - - return FanArtTvProvider.Current.DownloadSeriesXml(seriesDataPath, tvdbId, cancellationToken); - } - - /// <summary> - /// Dates the time to unix timestamp. - /// </summary> - /// <param name="dateTime">The date time.</param> - /// <returns>System.Double.</returns> - private static double DateTimeToUnixTimestamp(DateTime dateTime) - { - return (dateTime - new DateTime(1970, 1, 1).ToUniversalTime()).TotalSeconds; - } - - public class FanArtUpdate - { - public string id { get; set; } - public string name { get; set; } - public string new_images { get; set; } - public string total_images { get; set; } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs deleted file mode 100644 index 918930962..000000000 --- a/MediaBrowser.Controller/Providers/TV/RemoteEpisodeProvider.cs +++ /dev/null @@ -1,367 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using System; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - - /// <summary> - /// Class RemoteEpisodeProvider - /// </summary> - class RemoteEpisodeProvider : BaseMetadataProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="RemoteEpisodeProvider" /> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - public RemoteEpisodeProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - HttpClient = httpClient; - _providerManager = providerManager; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Episode; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get { return true; } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (HasLocalMeta(item)) - { - return false; - } - - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var episode = (Episode)item; - - var seriesId = episode.Series != null ? episode.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var seriesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower() + ".xml"); - - var seriesXmlFileInfo = new FileInfo(seriesXmlPath); - - return GetComparisonData(seriesXmlFileInfo); - } - - return Guid.Empty; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="seriesXmlFileInfo">The series XML file info.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(FileInfo seriesXmlFileInfo) - { - var date = seriesXmlFileInfo.Exists ? seriesXmlFileInfo.LastWriteTimeUtc : DateTime.MinValue; - - var key = date.Ticks + seriesXmlFileInfo.FullName; - - return key.GetMD5(); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - if (HasLocalMeta(item)) - { - return false; - } - - cancellationToken.ThrowIfCancellationRequested(); - - var episode = (Episode)item; - - var seriesId = episode.Series != null ? episode.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - var seriesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower() + ".xml"); - - var seriesXmlFileInfo = new FileInfo(seriesXmlPath); - - var status = ProviderRefreshStatus.Success; - - if (seriesXmlFileInfo.Exists) - { - var xmlDoc = new XmlDocument(); - xmlDoc.Load(seriesXmlPath); - - status = await FetchEpisodeData(xmlDoc, episode, seriesId, cancellationToken).ConfigureAwait(false); - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(seriesXmlFileInfo); - - SetLastRefreshed(item, DateTime.UtcNow, status); - return true; - } - - Logger.Info("Episode provider not fetching because series does not have a tvdb id: " + item.Path); - return false; - } - - - /// <summary> - /// Fetches the episode data. - /// </summary> - /// <param name="seriesXml">The series XML.</param> - /// <param name="episode">The episode.</param> - /// <param name="seriesId">The series id.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - private async Task<ProviderRefreshStatus> FetchEpisodeData(XmlDocument seriesXml, Episode episode, string seriesId, CancellationToken cancellationToken) - { - var status = ProviderRefreshStatus.Success; - - if (episode.IndexNumber == null) - { - return status; - } - - var seasonNumber = episode.ParentIndexNumber ?? TVUtils.GetSeasonNumberFromEpisodeFile(episode.Path); - - if (seasonNumber == null) - { - return status; - } - - var usingAbsoluteData = false; - - var episodeNode = seriesXml.SelectSingleNode("//Episode[EpisodeNumber='" + episode.IndexNumber.Value + "'][SeasonNumber='" + seasonNumber.Value + "']"); - - if (episodeNode == null) - { - if (seasonNumber.Value == 1) - { - episodeNode = seriesXml.SelectSingleNode("//Episode[absolute_number='" + episode.IndexNumber.Value + "']"); - usingAbsoluteData = true; - } - } - - // If still null, nothing we can do - if (episodeNode == null) - { - return status; - } - - var doc = new XmlDocument(); - doc.LoadXml(episodeNode.OuterXml); - - if (!episode.HasImage(ImageType.Primary)) - { - var p = doc.SafeGetString("//filename"); - if (p != null) - { - if (!Directory.Exists(episode.MetaLocation)) Directory.CreateDirectory(episode.MetaLocation); - - try - { - episode.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(episode, TVUtils.BannerUrl + p, Path.GetFileName(p), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken); - } - catch (HttpException) - { - status = ProviderRefreshStatus.CompletedWithErrors; - } - } - } - - episode.Overview = doc.SafeGetString("//Overview"); - if (usingAbsoluteData) - episode.IndexNumber = doc.SafeGetInt32("//absolute_number", -1); - if (episode.IndexNumber < 0) - episode.IndexNumber = doc.SafeGetInt32("//EpisodeNumber"); - - episode.Name = doc.SafeGetString("//EpisodeName"); - episode.CommunityRating = doc.SafeGetSingle("//Rating", -1, 10); - var firstAired = doc.SafeGetString("//FirstAired"); - DateTime airDate; - if (DateTime.TryParse(firstAired, out airDate) && airDate.Year > 1850) - { - episode.PremiereDate = airDate.ToUniversalTime(); - episode.ProductionYear = airDate.Year; - } - - episode.People.Clear(); - - var actors = doc.SafeGetString("//GuestStars"); - if (actors != null) - { - foreach (var person in actors.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(str => new PersonInfo { Type = PersonType.GuestStar, Name = str })) - { - episode.AddPerson(person); - } - } - - - var directors = doc.SafeGetString("//Director"); - if (directors != null) - { - foreach (var person in directors.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(str => new PersonInfo { Type = PersonType.Director, Name = str })) - { - episode.AddPerson(person); - } - } - - - var writers = doc.SafeGetString("//Writer"); - if (writers != null) - { - foreach (var person in writers.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) - .Where(i => !string.IsNullOrWhiteSpace(i)) - .Select(str => new PersonInfo { Type = PersonType.Writer, Name = str })) - { - episode.AddPerson(person); - } - } - - if (ConfigurationManager.Configuration.SaveLocalMeta) - { - if (!Directory.Exists(episode.MetaLocation)) Directory.CreateDirectory(episode.MetaLocation); - var ms = new MemoryStream(); - doc.Save(ms); - - await _providerManager.SaveToLibraryFilesystem(episode, Path.Combine(episode.MetaLocation, Path.GetFileNameWithoutExtension(episode.Path) + ".xml"), ms, cancellationToken).ConfigureAwait(false); - } - - return status; - } - - /// <summary> - /// Determines whether [has local meta] [the specified episode]. - /// </summary> - /// <param name="episode">The episode.</param> - /// <returns><c>true</c> if [has local meta] [the specified episode]; otherwise, <c>false</c>.</returns> - private bool HasLocalMeta(BaseItem episode) - { - return (episode.Parent.ResolveArgs.ContainsMetaFileByName(Path.GetFileNameWithoutExtension(episode.Path) + ".xml")); - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs deleted file mode 100644 index 5baaf9cd5..000000000 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs +++ /dev/null @@ -1,289 +0,0 @@ -using System.Net; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using MediaBrowser.Model.Net; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class RemoteSeasonProvider - /// </summary> - class RemoteSeasonProvider : BaseMetadataProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Initializes a new instance of the <see cref="RemoteSeasonProvider"/> class. - /// </summary> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <exception cref="System.ArgumentNullException">httpClient</exception> - public RemoteSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - _providerManager = providerManager; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Season; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - // Run after fanart - get { return MetadataProviderPriority.Fourth; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var season = (Season)item; - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - return GetComparisonData(imagesFileInfo); - } - - return Guid.Empty; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="imagesFileInfo">The images file info.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(FileInfo imagesFileInfo) - { - var date = imagesFileInfo.Exists ? imagesFileInfo.LastWriteTimeUtc : DateTime.MinValue; - - var key = date.Ticks + imagesFileInfo.FullName; - - return key.GetMD5(); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var season = (Season)item; - - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - if (imagesFileInfo.Exists) - { - if (!season.HasImage(ImageType.Primary) || !season.HasImage(ImageType.Banner) || season.BackdropImagePaths.Count == 0) - { - var xmlDoc = new XmlDocument(); - xmlDoc.Load(imagesXmlPath); - - await FetchImages(season, xmlDoc, cancellationToken).ConfigureAwait(false); - } - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(imagesFileInfo); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - return false; - } - - /// <summary> - /// Fetches the images. - /// </summary> - /// <param name="season">The season.</param> - /// <param name="images">The images.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchImages(Season season, XmlDocument images, CancellationToken cancellationToken) - { - var seasonNumber = season.IndexNumber ?? -1; - - if (seasonNumber == -1) - { - return; - } - - if (!season.HasImage(ImageType.Primary)) - { - var n = images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='season'][Season='" + seasonNumber + "'][Language='" + ConfigurationManager.Configuration.PreferredMetadataLanguage + "']") ?? - images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='season'][Season='" + seasonNumber + "'][Language='en']"); - if (n != null) - { - n = n.SelectSingleNode("./BannerPath"); - - if (n != null) - season.PrimaryImagePath = await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false); - } - } - - if (ConfigurationManager.Configuration.DownloadSeasonImages.Banner && !season.HasImage(ImageType.Banner)) - { - var n = images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='seasonwide'][Season='" + seasonNumber + "'][Language='" + ConfigurationManager.Configuration.PreferredMetadataLanguage + "']") ?? - images.SelectSingleNode("//Banner[BannerType='season'][BannerType2='seasonwide'][Season='" + seasonNumber + "'][Language='en']"); - if (n != null) - { - n = n.SelectSingleNode("./BannerPath"); - if (n != null) - { - try - { - var bannerImagePath = - await _providerManager.DownloadAndSaveImage(season, - TVUtils.BannerUrl + n.InnerText, - "banner" + - Path.GetExtension(n.InnerText), - ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken). - ConfigureAwait(false); - - season.SetImage(ImageType.Banner, bannerImagePath); - } - catch (HttpException ex) - { - Logger.ErrorException("Error downloading season banner for {0}", ex, season.Path); - - // Sometimes banners will come up not found even though they're reported in tvdb xml - if (ex.StatusCode.HasValue && ex.StatusCode.Value != HttpStatusCode.NotFound) - { - throw; - } - } - } - } - } - - if (ConfigurationManager.Configuration.DownloadSeasonImages.Backdrops && season.BackdropImagePaths.Count == 0) - { - var n = images.SelectSingleNode("//Banner[BannerType='fanart'][Season='" + seasonNumber + "']"); - if (n != null) - { - n = n.SelectSingleNode("./BannerPath"); - if (n != null) - { - season.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(season, TVUtils.BannerUrl + n.InnerText, "backdrop" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false)); - } - } - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs deleted file mode 100644 index aaa76a20c..000000000 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeriesProvider.cs +++ /dev/null @@ -1,639 +0,0 @@ -using System.Xml.Linq; -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using System; -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; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class RemoteSeriesProvider - /// </summary> - class RemoteSeriesProvider : BaseMetadataProvider, IDisposable - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// The tv db - /// </summary> - internal readonly SemaphoreSlim TvDbResourcePool = new SemaphoreSlim(2, 2); - - /// <summary> - /// Gets the current. - /// </summary> - /// <value>The current.</value> - internal static RemoteSeriesProvider Current { get; private set; } - - /// <summary> - /// The _zip client - /// </summary> - private readonly IZipClient _zipClient; - - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// Initializes a new instance of the <see cref="RemoteSeriesProvider" /> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <param name="zipClient">The zip client.</param> - /// <exception cref="System.ArgumentNullException">httpClient</exception> - public RemoteSeriesProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IZipClient zipClient) - : base(logManager, configurationManager) - { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; - _providerManager = providerManager; - _zipClient = zipClient; - Current = this; - } - - /// <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) - { - TvDbResourcePool.Dispose(); - } - } - - /// <summary> - /// The root URL - /// </summary> - private const string RootUrl = "http://www.thetvdb.com/api/"; - /// <summary> - /// The series query - /// </summary> - private const string SeriesQuery = "GetSeries.php?seriesname={0}"; - /// <summary> - /// The series get zip - /// </summary> - private const string SeriesGetZip = "http://www.thetvdb.com/api/{0}/series/{1}/all/{2}.zip"; - - /// <summary> - /// The LOCA l_ MET a_ FIL e_ NAME - /// </summary> - protected const string LocalMetaFileName = "Series.xml"; - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Series; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - // Refresh even if local metadata exists because we need episode infos - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var seriesId = item.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var path = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId); - - var files = new DirectoryInfo(path) - .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly) - .Select(i => i.FullName + i.LastWriteTimeUtc.Ticks) - .ToArray(); - - if (files.Length > 0) - { - return string.Join(string.Empty, files).GetMD5(); - } - } - - return Guid.Empty; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var series = (Series)item; - - var seriesId = series.GetProviderId(MetadataProviders.Tvdb); - - if (string.IsNullOrEmpty(seriesId)) - { - seriesId = await GetSeriesId(series, cancellationToken); - } - - cancellationToken.ThrowIfCancellationRequested(); - - if (!string.IsNullOrEmpty(seriesId)) - { - series.SetProviderId(MetadataProviders.Tvdb, seriesId); - - var seriesDataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId); - - await FetchSeriesData(series, seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false); - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(item); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - /// <summary> - /// Fetches the series data. - /// </summary> - /// <param name="series">The series.</param> - /// <param name="seriesId">The series id.</param> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - private async Task FetchSeriesData(Series series, string seriesId, string seriesDataPath, CancellationToken cancellationToken) - { - var files = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly).Select(Path.GetFileName).ToArray(); - - var seriesXmlFilename = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower() + ".xml"; - - // Only download if not already there - // The prescan task will take care of updates so we don't need to re-download here - if (!files.Contains("banners.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains("actors.xml", StringComparer.OrdinalIgnoreCase) || !files.Contains(seriesXmlFilename, StringComparer.OrdinalIgnoreCase)) - { - await DownloadSeriesZip(seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false); - } - - // Only examine the main info if there's no local metadata - if (!HasLocalMeta(series)) - { - var seriesXmlPath = Path.Combine(seriesDataPath, seriesXmlFilename); - var actorsXmlPath = Path.Combine(seriesDataPath, "actors.xml"); - - var seriesDoc = new XmlDocument(); - seriesDoc.Load(seriesXmlPath); - - FetchMainInfo(series, seriesDoc); - - var actorsDoc = new XmlDocument(); - actorsDoc.Load(actorsXmlPath); - - FetchActors(series, actorsDoc, seriesDoc); - - if (ConfigurationManager.Configuration.SaveLocalMeta) - { - var ms = new MemoryStream(); - seriesDoc.Save(ms); - - await _providerManager.SaveToLibraryFilesystem(series, Path.Combine(series.MetaLocation, LocalMetaFileName), ms, cancellationToken).ConfigureAwait(false); - } - } - } - - /// <summary> - /// Downloads the series zip. - /// </summary> - /// <param name="seriesId">The series id.</param> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - internal async Task DownloadSeriesZip(string seriesId, string seriesDataPath, CancellationToken cancellationToken) - { - var url = string.Format(SeriesGetZip, TVUtils.TvdbApiKey, seriesId, ConfigurationManager.Configuration.PreferredMetadataLanguage); - - using (var zipStream = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - // Copy to memory stream because we need a seekable stream - using (var ms = new MemoryStream()) - { - await zipStream.CopyToAsync(ms).ConfigureAwait(false); - - ms.Position = 0; - _zipClient.ExtractAll(ms, seriesDataPath, true); - } - } - } - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <param name="seriesId">The series id.</param> - /// <returns>System.String.</returns> - internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId) - { - var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); - - if (!Directory.Exists(seriesDataPath)) - { - Directory.CreateDirectory(seriesDataPath); - } - - return seriesDataPath; - } - - /// <summary> - /// Gets the series data path. - /// </summary> - /// <param name="appPaths">The app paths.</param> - /// <returns>System.String.</returns> - internal static string GetSeriesDataPath(IApplicationPaths appPaths) - { - var dataPath = Path.Combine(appPaths.DataPath, "tvdb"); - - if (!Directory.Exists(dataPath)) - { - Directory.CreateDirectory(dataPath); - } - - return dataPath; - } - - /// <summary> - /// Fetches the main info. - /// </summary> - /// <param name="series">The series.</param> - /// <param name="doc">The doc.</param> - private void FetchMainInfo(Series series, XmlDocument doc) - { - series.Name = doc.SafeGetString("//SeriesName"); - series.Overview = doc.SafeGetString("//Overview"); - series.CommunityRating = doc.SafeGetSingle("//Rating", 0, 10); - series.AirDays = TVUtils.GetAirDays(doc.SafeGetString("//Airs_DayOfWeek")); - series.AirTime = doc.SafeGetString("//Airs_Time"); - SeriesStatus seriesStatus; - if(Enum.TryParse(doc.SafeGetString("//Status"), true, out seriesStatus)) - series.Status = seriesStatus; - series.PremiereDate = doc.SafeGetDateTime("//FirstAired"); - if (series.PremiereDate.HasValue) - series.ProductionYear = series.PremiereDate.Value.Year; - //Runtime is in minutes, and 1 tick = 10000 ms - series.RunTimeTicks = doc.SafeGetInt32("//Runtime") * 6; - - string s = doc.SafeGetString("//Network"); - - if (!string.IsNullOrWhiteSpace(s)) - { - series.Studios.Clear(); - - foreach (var studio in s.Trim().Split('|')) - { - series.AddStudio(studio); - } - } - - series.OfficialRating = doc.SafeGetString("//ContentRating"); - - string g = doc.SafeGetString("//Genre"); - - if (g != null) - { - string[] genres = g.Trim('|').Split('|'); - if (g.Length > 0) - { - series.Genres.Clear(); - - foreach (var genre in genres) - { - series.AddGenre(genre); - } - } - } - - if (series.Status == SeriesStatus.Ended) { - - var document = XDocument.Load(new XmlNodeReader(doc)); - var dates = document.Descendants("Episode").Where(x => { - var seasonNumber = x.Element("SeasonNumber"); - var firstAired = x.Element("FirstAired"); - return firstAired != null && seasonNumber != null && (!string.IsNullOrEmpty(seasonNumber.Value) && seasonNumber.Value != "0") && !string.IsNullOrEmpty(firstAired.Value); - }).Select(x => { - DateTime? date = null; - DateTime tempDate; - var firstAired = x.Element("FirstAired"); - if (firstAired != null && DateTime.TryParse(firstAired.Value, out tempDate)) - { - date = tempDate; - } - return date; - }).ToList(); - if(dates.Any(x=>x.HasValue)) - series.EndDate = dates.Where(x => x.HasValue).Max(); - } - } - - /// <summary> - /// Fetches the actors. - /// </summary> - /// <param name="series">The series.</param> - /// <param name="actorsDoc">The actors doc.</param> - /// <param name="seriesDoc">The seriesDoc.</param> - /// <returns>Task.</returns> - private void FetchActors(Series series, XmlDocument actorsDoc, XmlDocument seriesDoc) - { - XmlNode actorsNode = null; - if (ConfigurationManager.Configuration.SaveLocalMeta) - { - //add to the main seriesDoc for saving - var seriesNode = seriesDoc.SelectSingleNode("//Series"); - if (seriesNode != null) - { - actorsNode = seriesDoc.CreateNode(XmlNodeType.Element, "Persons", null); - seriesNode.AppendChild(actorsNode); - } - } - - var xmlNodeList = actorsDoc.SelectNodes("Actors/Actor"); - - if (xmlNodeList != null) - { - series.People.Clear(); - - foreach (XmlNode p in xmlNodeList) - { - string actorName = p.SafeGetString("Name"); - string actorRole = p.SafeGetString("Role"); - if (!string.IsNullOrWhiteSpace(actorName)) - { - series.AddPerson(new PersonInfo { Type = PersonType.Actor, Name = actorName, Role = actorRole }); - - if (ConfigurationManager.Configuration.SaveLocalMeta && actorsNode != null) - { - //create in main seriesDoc - var personNode = seriesDoc.CreateNode(XmlNodeType.Element, "Person", null); - foreach (XmlNode subNode in p.ChildNodes) - personNode.AppendChild(seriesDoc.ImportNode(subNode, true)); - //need to add the type - var typeNode = seriesDoc.CreateNode(XmlNodeType.Element, "Type", null); - typeNode.InnerText = PersonType.Actor; - personNode.AppendChild(typeNode); - actorsNode.AppendChild(personNode); - } - - } - } - } - } - - /// <summary> - /// The us culture - /// </summary> - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Determines whether [has local meta] [the specified item]. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if [has local meta] [the specified item]; otherwise, <c>false</c>.</returns> - private bool HasLocalMeta(BaseItem item) - { - return item.ResolveArgs.ContainsMetaFileByName(LocalMetaFileName); - } - - /// <summary> - /// Gets the series id. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - private async Task<string> GetSeriesId(BaseItem item, CancellationToken cancellationToken) - { - var seriesId = item.GetProviderId(MetadataProviders.Tvdb); - if (string.IsNullOrEmpty(seriesId)) - { - seriesId = await FindSeries(item.Name, cancellationToken).ConfigureAwait(false); - } - return seriesId; - } - - - /// <summary> - /// Finds the series. - /// </summary> - /// <param name="name">The name.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.String}.</returns> - public async Task<string> FindSeries(string name, CancellationToken cancellationToken) - { - - //nope - search for it - string url = string.Format(RootUrl + SeriesQuery, WebUtility.UrlEncode(name)); - var doc = new XmlDocument(); - - using (var results = await HttpClient.Get(new HttpRequestOptions - { - Url = url, - ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken - - }).ConfigureAwait(false)) - { - doc.Load(results); - } - - if (doc.HasChildNodes) - { - XmlNodeList nodes = doc.SelectNodes("//Series"); - string comparableName = GetComparableName(name); - if (nodes != null) - foreach (XmlNode node in nodes) - { - var n = node.SelectSingleNode("./SeriesName"); - if (n != null && GetComparableName(n.InnerText) == comparableName) - { - n = node.SelectSingleNode("./seriesid"); - if (n != null) - return n.InnerText; - } - else - { - if (n != null) - Logger.Info("TVDb Provider - " + n.InnerText + " did not match " + comparableName); - } - } - } - - Logger.Info("TVDb Provider - Could not find " + name + ". Check name on Thetvdb.org."); - return null; - } - - /// <summary> - /// The remove - /// </summary> - const string remove = "\"'!`?"; - /// <summary> - /// The spacers - /// </summary> - const string spacers = "/,.:;\\(){}[]+-_=–*"; // (there are not actually two - in the they are different char codes) - - /// <summary> - /// Gets the name of the comparable. - /// </summary> - /// <param name="name">The name.</param> - /// <returns>System.String.</returns> - internal static string GetComparableName(string name) - { - name = name.ToLower(); - name = name.Normalize(NormalizationForm.FormKD); - var sb = new StringBuilder(); - foreach (var c in name) - { - if ((int)c >= 0x2B0 && (int)c <= 0x0333) - { - // skip char modifier and diacritics - } - else if (remove.IndexOf(c) > -1) - { - // skip chars we are removing - } - else if (spacers.IndexOf(c) > -1) - { - sb.Append(" "); - } - else if (c == '&') - { - sb.Append(" and "); - } - else - { - sb.Append(c); - } - } - name = sb.ToString(); - name = name.Replace(", the", ""); - name = name.Replace("the ", " "); - name = name.Replace(" the ", " "); - - string prevName; - do - { - prevName = name; - name = name.Replace(" ", " "); - } while (name.Length != prevName.Length); - - return name.Trim(); - } - - /// <summary> - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. - /// </summary> - public void Dispose() - { - Dispose(true); - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs b/MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs deleted file mode 100644 index 786793069..000000000 --- a/MediaBrowser.Controller/Providers/TV/SeriesProviderFromXml.cs +++ /dev/null @@ -1,100 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Model.Entities; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using MediaBrowser.Model.Logging; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class SeriesProviderFromXml - /// </summary> - public class SeriesProviderFromXml : BaseMetadataProvider - { - public SeriesProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager) : base(logManager, configurationManager) - { - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Series && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - - /// <summary> - /// Override this to return the date that should be compared to the last refresh date - /// to determine if this provider should be re-fetched. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>DateTime.</returns> - protected override DateTime CompareDate(BaseItem item) - { - var entry = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "series.xml")); - return entry != null ? entry.LastWriteTimeUtc : DateTime.MinValue; - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - return Fetch(item, cancellationToken); - } - - /// <summary> - /// Fetches the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private async Task<bool> Fetch(BaseItem item, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, "series.xml")); - - if (metadataFile != null) - { - var path = metadataFile.FullName; - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - new SeriesXmlParser(Logger).Fetch((Series)item, path, cancellationToken); - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow); - - return true; - } - - return false; - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs b/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs deleted file mode 100644 index c03e2a7f5..000000000 --- a/MediaBrowser.Controller/Providers/TV/SeriesXmlParser.cs +++ /dev/null @@ -1,106 +0,0 @@ -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class SeriesXmlParser - /// </summary> - public class SeriesXmlParser : BaseItemXmlParser<Series> - { - /// <summary> - /// Initializes a new instance of the <see cref="BaseItemXmlParser{T}" /> class. - /// </summary> - /// <param name="logger">The logger.</param> - public SeriesXmlParser(ILogger logger) - : base(logger) - { - } - - /// <summary> - /// Fetches the data from XML node. - /// </summary> - /// <param name="reader">The reader.</param> - /// <param name="item">The item.</param> - protected override void FetchDataFromXmlNode(XmlReader reader, Series item) - { - switch (reader.Name) - { - case "Series": - //MB generated metadata is within a "Series" node - using (var subTree = reader.ReadSubtree()) - { - subTree.MoveToContent(); - - // Loop through each element - while (subTree.Read()) - { - if (subTree.NodeType == XmlNodeType.Element) - { - FetchDataFromXmlNode(subTree, item); - } - } - - } - break; - - case "id": - string id = reader.ReadElementContentAsString(); - if (!string.IsNullOrWhiteSpace(id)) - { - item.SetProviderId(MetadataProviders.Tvdb, id); - } - break; - - case "Airs_DayOfWeek": - { - item.AirDays = TVUtils.GetAirDays(reader.ReadElementContentAsString()); - break; - } - - case "Airs_Time": - { - var val = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(val)) - { - item.AirTime = val; - } - break; - } - - case "SeriesName": - item.Name = reader.ReadElementContentAsString(); - break; - - case "Status": - { - var status = reader.ReadElementContentAsString(); - - if (!string.IsNullOrWhiteSpace(status)) - { - SeriesStatus seriesStatus; - if (Enum.TryParse(status, true, out seriesStatus)) - { - item.Status = seriesStatus; - } - else - { - Logger.Info("Unrecognized series status: " + status); - } - } - - break; - } - - default: - base.FetchDataFromXmlNode(reader, item); - break; - } - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/TvdbPrescanTask.cs b/MediaBrowser.Controller/Providers/TV/TvdbPrescanTask.cs deleted file mode 100644 index dbd45245f..000000000 --- a/MediaBrowser.Controller/Providers/TV/TvdbPrescanTask.cs +++ /dev/null @@ -1,223 +0,0 @@ -using MediaBrowser.Common.Configuration; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Extensions; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - /// <summary> - /// Class TvdbPrescanTask - /// </summary> - public class TvdbPrescanTask : ILibraryPrescanTask - { - /// <summary> - /// The server time URL - /// </summary> - private const string ServerTimeUrl = "http://thetvdb.com/api/Updates.php?type=none"; - - /// <summary> - /// The updates URL - /// </summary> - private const string UpdatesUrl = "http://thetvdb.com/api/Updates.php?type=all&time={0}"; - - /// <summary> - /// The _HTTP client - /// </summary> - private readonly IHttpClient _httpClient; - /// <summary> - /// The _logger - /// </summary> - private readonly ILogger _logger; - /// <summary> - /// The _config - /// </summary> - private readonly IServerConfigurationManager _config; - - /// <summary> - /// Initializes a new instance of the <see cref="TvdbPrescanTask"/> class. - /// </summary> - /// <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) - { - _logger = logger; - _httpClient = httpClient; - _config = config; - } - - /// <summary> - /// Runs the specified progress. - /// </summary> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public async Task Run(IProgress<double> progress, CancellationToken cancellationToken) - { - if (!_config.Configuration.EnableInternetProviders) - { - progress.Report(100); - return; - } - - var path = RemoteSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths); - - var timestampFile = Path.Combine(path, "time.txt"); - - var timestampFileInfo = new FileInfo(timestampFile); - - // Don't check for tvdb updates anymore frequently than 24 hours - if (timestampFileInfo.Exists && (DateTime.UtcNow - timestampFileInfo.LastWriteTimeUtc).TotalDays < 1) - { - return; - } - - // Find out the last time we queried tvdb for updates - var lastUpdateTime = timestampFileInfo.Exists ? File.ReadAllText(timestampFile, Encoding.UTF8) : string.Empty; - - string newUpdateTime; - - var existingDirectories = Directory.EnumerateDirectories(path).Select(Path.GetFileName).ToList(); - - // If this is our first time, update all series - if (string.IsNullOrEmpty(lastUpdateTime)) - { - // First get tvdb server time - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = ServerTimeUrl, - CancellationToken = cancellationToken, - EnableHttpCompression = true, - ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool - - }).ConfigureAwait(false)) - { - var doc = new XmlDocument(); - - doc.Load(stream); - - newUpdateTime = doc.SafeGetString("//Time"); - } - - await UpdateSeries(existingDirectories, path, progress, cancellationToken).ConfigureAwait(false); - } - else - { - var seriesToUpdate = await GetSeriesIdsToUpdate(existingDirectories, lastUpdateTime, cancellationToken).ConfigureAwait(false); - - newUpdateTime = seriesToUpdate.Item2; - - await UpdateSeries(seriesToUpdate.Item1, path, progress, cancellationToken).ConfigureAwait(false); - } - - File.WriteAllText(timestampFile, newUpdateTime, Encoding.UTF8); - progress.Report(100); - } - - /// <summary> - /// Gets the series ids to update. - /// </summary> - /// <param name="existingSeriesIds">The existing series ids.</param> - /// <param name="lastUpdateTime">The last update time.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{IEnumerable{System.String}}.</returns> - private async Task<Tuple<IEnumerable<string>, string>> GetSeriesIdsToUpdate(IEnumerable<string> existingSeriesIds, string lastUpdateTime, CancellationToken cancellationToken) - { - // First get last time - using (var stream = await _httpClient.Get(new HttpRequestOptions - { - Url = string.Format(UpdatesUrl, lastUpdateTime), - CancellationToken = cancellationToken, - EnableHttpCompression = true, - ResourcePool = RemoteSeriesProvider.Current.TvDbResourcePool - - }).ConfigureAwait(false)) - { - var doc = new XmlDocument(); - - doc.Load(stream); - - var newUpdateTime = doc.SafeGetString("//Time"); - - var seriesNodes = doc.SelectNodes("//Series"); - - var seriesList = seriesNodes == null ? new string[] { } : - seriesNodes.Cast<XmlNode>() - .Select(i => i.InnerText) - .Where(i => !string.IsNullOrWhiteSpace(i) && existingSeriesIds.Contains(i, StringComparer.OrdinalIgnoreCase)); - - return new Tuple<IEnumerable<string>, string>(seriesList, newUpdateTime); - } - } - - /// <summary> - /// Updates the series. - /// </summary> - /// <param name="seriesIds">The series ids.</param> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="progress">The progress.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task UpdateSeries(IEnumerable<string> seriesIds, string seriesDataPath, IProgress<double> progress, CancellationToken cancellationToken) - { - var list = seriesIds.ToList(); - var numComplete = 0; - - foreach (var seriesId in list) - { - try - { - await UpdateSeries(seriesId, seriesDataPath, cancellationToken).ConfigureAwait(false); - } - catch (HttpException ex) - { - // Already logged at lower levels, but don't fail the whole operation, unless timed out - // We have to fail this to make it run again otherwise new episode data could potentially be missing - if (ex.IsTimedOut) - { - throw; - } - } - - numComplete++; - double percent = numComplete; - percent /= list.Count; - percent *= 100; - - progress.Report(percent); - } - } - - /// <summary> - /// Updates the series. - /// </summary> - /// <param name="id">The id.</param> - /// <param name="seriesDataPath">The series data path.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private Task UpdateSeries(string id, string seriesDataPath, CancellationToken cancellationToken) - { - _logger.Info("Updating series " + id); - - seriesDataPath = Path.Combine(seriesDataPath, id); - - if (!Directory.Exists(seriesDataPath)) - { - Directory.CreateDirectory(seriesDataPath); - } - - return RemoteSeriesProvider.Current.DownloadSeriesZip(id, seriesDataPath, cancellationToken); - } - } -} diff --git a/MediaBrowser.Controller/Providers/TV/TvdbSeriesImageProvider.cs b/MediaBrowser.Controller/Providers/TV/TvdbSeriesImageProvider.cs deleted file mode 100644 index 68294a17b..000000000 --- a/MediaBrowser.Controller/Providers/TV/TvdbSeriesImageProvider.cs +++ /dev/null @@ -1,283 +0,0 @@ -using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Globalization; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; - -namespace MediaBrowser.Controller.Providers.TV -{ - public class TvdbSeriesImageProvider : BaseMetadataProvider - { - /// <summary> - /// Gets the HTTP client. - /// </summary> - /// <value>The HTTP client.</value> - protected IHttpClient HttpClient { get; private set; } - - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - - /// <summary> - /// Initializes a new instance of the <see cref="TvdbSeriesImageProvider"/> class. - /// </summary> - /// <param name="httpClient">The HTTP client.</param> - /// <param name="logManager">The log manager.</param> - /// <param name="configurationManager">The configuration manager.</param> - /// <param name="providerManager">The provider manager.</param> - /// <exception cref="System.ArgumentNullException">httpClient</exception> - public TvdbSeriesImageProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) - : base(logManager, configurationManager) - { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; - _providerManager = providerManager; - } - - /// <summary> - /// Supportses the specified item. - /// </summary> - /// <param name="item">The item.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - public override bool Supports(BaseItem item) - { - return item is Series; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - // Run after fanart - get { return MetadataProviderPriority.Fourth; } - } - - /// <summary> - /// Gets a value indicating whether [requires internet]. - /// </summary> - /// <value><c>true</c> if [requires internet]; otherwise, <c>false</c>.</value> - public override bool RequiresInternet - { - get - { - return true; - } - } - - /// <summary> - /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes - /// </summary> - /// <value><c>true</c> if [refresh on file system stamp change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnFileSystemStampChange - { - get - { - return ConfigurationManager.Configuration.SaveLocalMeta; - } - } - - /// <summary> - /// Gets a value indicating whether [refresh on version change]. - /// </summary> - /// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value> - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion - { - get - { - return "1"; - } - } - - /// <summary> - /// Needses the refresh internal. - /// </summary> - /// <param name="item">The item.</param> - /// <param name="providerInfo">The provider info.</param> - /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (GetComparisonData(item) != providerInfo.Data) - { - return true; - } - - return base.NeedsRefreshInternal(item, providerInfo); - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="item">The item.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(BaseItem item) - { - var seriesId = item.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - return GetComparisonData(imagesFileInfo); - } - - return Guid.Empty; - } - - /// <summary> - /// Gets the comparison data. - /// </summary> - /// <param name="imagesFileInfo">The images file info.</param> - /// <returns>Guid.</returns> - private Guid GetComparisonData(FileInfo imagesFileInfo) - { - var date = imagesFileInfo.Exists ? imagesFileInfo.LastWriteTimeUtc : DateTime.MinValue; - - var key = date.Ticks + imagesFileInfo.FullName; - - return key.GetMD5(); - } - - /// <summary> - /// Fetches metadata and returns true or false indicating if any work that requires persistence was done - /// </summary> - /// <param name="item">The item.</param> - /// <param name="force">if set to <c>true</c> [force].</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override async Task<bool> FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var series = (Series)item; - var seriesId = series.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(seriesId)) - { - // Process images - var imagesXmlPath = Path.Combine(RemoteSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - if (imagesFileInfo.Exists) - { - if (!series.HasImage(ImageType.Primary) || !series.HasImage(ImageType.Banner) || series.BackdropImagePaths.Count == 0) - { - var xmlDoc = new XmlDocument(); - xmlDoc.Load(imagesXmlPath); - - await FetchImages(series, xmlDoc, cancellationToken).ConfigureAwait(false); - } - } - - BaseProviderInfo data; - if (!item.ProviderData.TryGetValue(Id, out data)) - { - data = new BaseProviderInfo(); - item.ProviderData[Id] = data; - } - - data.Data = GetComparisonData(imagesFileInfo); - - SetLastRefreshed(item, DateTime.UtcNow); - return true; - } - - return false; - } - - protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); - - /// <summary> - /// Fetches the images. - /// </summary> - /// <param name="series">The series.</param> - /// <param name="images">The images.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - private async Task FetchImages(Series series, XmlDocument images, CancellationToken cancellationToken) - { - if (!series.HasImage(ImageType.Primary)) - { - var n = images.SelectSingleNode("//Banner[BannerType='poster']"); - if (n != null) - { - n = n.SelectSingleNode("./BannerPath"); - if (n != null) - { - var path = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "folder" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false); - - series.SetImage(ImageType.Primary, path); - } - } - } - - if (ConfigurationManager.Configuration.DownloadSeriesImages.Banner && !series.HasImage(ImageType.Banner)) - { - var n = images.SelectSingleNode("//Banner[BannerType='series']"); - if (n != null) - { - n = n.SelectSingleNode("./BannerPath"); - if (n != null) - { - var bannerImagePath = await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + n.InnerText, "banner" + Path.GetExtension(n.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken); - - series.SetImage(ImageType.Banner, bannerImagePath); - } - } - } - - if (series.BackdropImagePaths.Count == 0) - { - var bdNo = series.BackdropImagePaths.Count; - - var xmlNodeList = images.SelectNodes("//Banner[BannerType='fanart']"); - if (xmlNodeList != null) - { - foreach (XmlNode b in xmlNodeList) - { - var p = b.SelectSingleNode("./BannerPath"); - - if (p != null) - { - var bdName = "backdrop" + (bdNo > 0 ? bdNo.ToString(UsCulture) : ""); - series.BackdropImagePaths.Add(await _providerManager.DownloadAndSaveImage(series, TVUtils.BannerUrl + p.InnerText, bdName + Path.GetExtension(p.InnerText), ConfigurationManager.Configuration.SaveLocalMeta, RemoteSeriesProvider.Current.TvDbResourcePool, cancellationToken).ConfigureAwait(false)); - bdNo++; - } - - if (series.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops) break; - } - } - } - } - } -} |
