From a0c5d27e85b23cf57fc02c4cf572e5a36df797c5 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 24 May 2013 12:51:42 -0400 Subject: [PATCH] fixes #174 - Support all fanart tv images --- .../MediaBrowser.Controller.csproj | 1 + .../Providers/Movies/FanArtMovieProvider.cs | 3 +- .../Movies/OpenMovieDatabaseProvider.cs | 3 +- .../Providers/Music/FanArtAlbumProvider.cs | 10 +- .../Providers/Music/FanArtArtistProvider.cs | 10 +- .../Providers/TV/FanArtSeasonProvider.cs | 209 ++++++++++++++++++ .../Providers/TV/FanArtTVProvider.cs | 88 +++++++- .../Providers/TV/RemoteSeasonProvider.cs | 17 +- 8 files changed, 299 insertions(+), 42 deletions(-) create mode 100644 MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj index 9f345e06d..236c8b520 100644 --- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj +++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj @@ -74,6 +74,7 @@ + diff --git a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs index 4254b8fd1..00d6c72ca 100644 --- a/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/FanArtMovieProvider.cs @@ -179,8 +179,7 @@ namespace MediaBrowser.Controller.Providers.Movies { Url = url, ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + CancellationToken = cancellationToken }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs b/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs index 28f56d64f..ec33a1714 100644 --- a/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs +++ b/MediaBrowser.Controller/Providers/Movies/OpenMovieDatabaseProvider.cs @@ -159,8 +159,7 @@ namespace MediaBrowser.Controller.Providers.Movies { Url = url, ResourcePool = _resourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + CancellationToken = cancellationToken }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs index 570bcbe9e..9f3befae4 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs @@ -142,8 +142,7 @@ namespace MediaBrowser.Controller.Providers.Music { Url = url, ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + CancellationToken = cancellationToken }).ConfigureAwait(false)) { @@ -154,7 +153,7 @@ namespace MediaBrowser.Controller.Providers.Music if (doc.HasChildNodes) { - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.ResolveArgs.ContainsMetaFileByName(DiscFile)) + if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Disc && !item.HasImage(ImageType.Disc)) { var node = doc.SelectSingleNode("//fanart/music/albums/album/cdart/@url"); @@ -167,7 +166,7 @@ namespace MediaBrowser.Controller.Providers.Music } } - if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.ResolveArgs.ContainsMetaFileByName(PrimaryFile)) + if (ConfigurationManager.Configuration.DownloadMusicAlbumImages.Primary && !item.HasImage(ImageType.Primary)) { var node = doc.SelectSingleNode("//fanart/music/albums/album/albumcover/@url"); @@ -220,8 +219,7 @@ namespace MediaBrowser.Controller.Providers.Music { Url = url, CancellationToken = cancellationToken, - UserAgent = Environment.MachineName, - EnableResponseCache = true + UserAgent = Environment.MachineName }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs index fb09e4a7f..02f531cb0 100644 --- a/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs +++ b/MediaBrowser.Controller/Providers/Music/FanArtArtistProvider.cs @@ -4,7 +4,6 @@ using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; using System; using System.Collections.Generic; using System.Globalization; @@ -123,8 +122,7 @@ namespace MediaBrowser.Controller.Providers.Music { Url = url, ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + CancellationToken = cancellationToken }).ConfigureAwait(false)) { @@ -176,7 +174,7 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && !item.ResolveArgs.ContainsMetaFileByName(ArtFile)) + if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Art && !item.HasImage(ImageType.Art)) { var node = doc.SelectSingleNode("//fanart/music/musicarts/" + hd + "musicart/@url") ?? @@ -190,7 +188,7 @@ namespace MediaBrowser.Controller.Providers.Music } cancellationToken.ThrowIfCancellationRequested(); - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Banner && !item.ResolveArgs.ContainsMetaFileByName(BannerFile)) + 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"); @@ -205,7 +203,7 @@ namespace MediaBrowser.Controller.Providers.Music cancellationToken.ThrowIfCancellationRequested(); // Artist thumbs are actually primary images (they are square/portrait) - if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary && !item.ResolveArgs.ContainsMetaFileByName(PrimaryFile)) + if (ConfigurationManager.Configuration.DownloadMusicArtistImages.Primary && !item.HasImage(ImageType.Primary)) { var node = doc.SelectSingleNode("//fanart/music/artistthumbs/artistthumb/@url"); path = node != null ? node.Value : null; diff --git a/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs new file mode 100644 index 000000000..36eb09242 --- /dev/null +++ b/MediaBrowser.Controller/Providers/TV/FanArtSeasonProvider.cs @@ -0,0 +1,209 @@ +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; + +namespace MediaBrowser.Controller.Providers.TV +{ + class FanArtSeasonProvider : FanartBaseProvider + { + /// + /// The _provider manager + /// + private readonly IProviderManager _providerManager; + + public FanArtSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + : base(logManager, configurationManager) + { + _providerManager = providerManager; + } + + public override bool Supports(BaseItem item) + { + return item is Season; + } + + /// + /// Needses the refresh internal. + /// + /// The item. + /// The provider info. + /// true if XXXX, false otherwise + protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) + { + if (GetComparisonData(item) != providerInfo.Data) + { + return true; + } + + return base.NeedsRefreshInternal(item, providerInfo); + } + + /// + /// Gets the comparison data. + /// + /// The item. + /// Guid. + 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; + } + + /// + /// Gets the comparison data. + /// + /// The images file info. + /// Guid. + private Guid GetComparisonData(FileInfo imagesFileInfo) + { + var date = imagesFileInfo.Exists ? imagesFileInfo.LastWriteTimeUtc : DateTime.MinValue; + + var key = date.Ticks + imagesFileInfo.FullName; + + return key.GetMD5(); + } + + public override async Task 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.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; + } + + /// + /// Fetches the images. + /// + /// The season. + /// The doc. + /// The cancellation token. + /// Task. + 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)); + } + } + } + + /// + /// Gets a value indicating whether [requires internet]. + /// + /// true if [requires internet]; otherwise, false. + public override bool RequiresInternet + { + get + { + return true; + } + } + + /// + /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes + /// + /// true if [refresh on file system stamp change]; otherwise, false. + protected override bool RefreshOnFileSystemStampChange + { + get + { + return ConfigurationManager.Configuration.SaveLocalMeta; + } + } + + /// + /// Gets a value indicating whether [refresh on version change]. + /// + /// true if [refresh on version change]; otherwise, false. + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + /// + /// Gets the provider version. + /// + /// The provider version. + protected override string ProviderVersion + { + get + { + return "3"; + } + } + } +} diff --git a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs index 2a8067f6a..e5e66e942 100644 --- a/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/FanArtTVProvider.cs @@ -1,5 +1,6 @@ -using System.Globalization; +using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Extensions; +using MediaBrowser.Common.IO; using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; @@ -7,6 +8,8 @@ using MediaBrowser.Controller.Entities.TV; 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; @@ -80,6 +83,65 @@ namespace MediaBrowser.Controller.Providers.TV return string.IsNullOrEmpty(id) ? Guid.Empty : id.GetMD5(); } + /// + /// Gets a value indicating whether [refresh on version change]. + /// + /// true if [refresh on version change]; otherwise, false. + protected override bool RefreshOnVersionChange + { + get + { + return true; + } + } + + /// + /// Gets the provider version. + /// + /// The provider version. + protected override string ProviderVersion + { + get + { + return "1"; + } + } + + /// + /// Gets the series data path. + /// + /// The app paths. + /// The series id. + /// System.String. + internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId) + { + var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId); + + if (!Directory.Exists(seriesDataPath)) + { + Directory.CreateDirectory(seriesDataPath); + } + + return seriesDataPath; + } + + /// + /// Gets the series data path. + /// + /// The app paths. + /// System.String. + 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 FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken) @@ -98,21 +160,29 @@ namespace MediaBrowser.Controller.Providers.TV var series = (Series)item; string language = ConfigurationManager.Configuration.PreferredMetadataLanguage.ToLower(); - string url = string.Format(FanArtBaseUrl, ApiKey, series.GetProviderId(MetadataProviders.Tvdb)); - var doc = new XmlDocument(); - using (var xml = await HttpClient.Get(new HttpRequestOptions + var seriesId = series.GetProviderId(MetadataProviders.Tvdb); + string url = string.Format(FanArtBaseUrl, ApiKey, seriesId); + + var xmlPath = Path.Combine(GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "fanart.xml"); + + using (var response = await HttpClient.Get(new HttpRequestOptions { Url = url, ResourcePool = FanArtResourcePool, - CancellationToken = cancellationToken, - EnableResponseCache = true + CancellationToken = cancellationToken }).ConfigureAwait(false)) { - doc.Load(xml); + using (var xmlFileStream = new FileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, StreamDefaults.DefaultFileStreamBufferSize, FileOptions.Asynchronous)) + { + await response.CopyToAsync(xmlFileStream).ConfigureAwait(false); + } } + var doc = new XmlDocument(); + doc.Load(xmlPath); + cancellationToken.ThrowIfCancellationRequested(); string path; @@ -126,7 +196,6 @@ namespace MediaBrowser.Controller.Providers.TV path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting ClearLogo for " + series.Name); series.SetImage(ImageType.Logo, await _providerManager.DownloadAndSaveImage(series, path, LogoFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } } @@ -143,7 +212,6 @@ namespace MediaBrowser.Controller.Providers.TV path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting ClearArt for " + series.Name); series.SetImage(ImageType.Art, await _providerManager.DownloadAndSaveImage(series, path, ArtFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } } @@ -157,7 +225,6 @@ namespace MediaBrowser.Controller.Providers.TV path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting ThumbArt for " + series.Name); series.SetImage(ImageType.Thumb, await _providerManager.DownloadAndSaveImage(series, path, ThumbFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } } @@ -169,7 +236,6 @@ namespace MediaBrowser.Controller.Providers.TV path = node != null ? node.Value : null; if (!string.IsNullOrEmpty(path)) { - Logger.Debug("FanArtProvider getting banner for " + series.Name); series.SetImage(ImageType.Banner, await _providerManager.DownloadAndSaveImage(series, path, BannerFile, ConfigurationManager.Configuration.SaveLocalMeta, FanArtResourcePool, cancellationToken).ConfigureAwait(false)); } } diff --git a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs index 585c24b14..5e1f5f8dd 100644 --- a/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs +++ b/MediaBrowser.Controller/Providers/TV/RemoteSeasonProvider.cs @@ -1,5 +1,4 @@ using MediaBrowser.Common.Extensions; -using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -19,12 +18,6 @@ namespace MediaBrowser.Controller.Providers.TV /// class RemoteSeasonProvider : BaseMetadataProvider { - /// - /// Gets the HTTP client. - /// - /// The HTTP client. - protected IHttpClient HttpClient { get; private set; } - /// /// The _provider manager /// @@ -33,19 +26,13 @@ namespace MediaBrowser.Controller.Providers.TV /// /// Initializes a new instance of the class. /// - /// The HTTP client. /// The log manager. /// The configuration manager. /// The provider manager. /// httpClient - public RemoteSeasonProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) + public RemoteSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager) : base(logManager, configurationManager) { - if (httpClient == null) - { - throw new ArgumentNullException("httpClient"); - } - HttpClient = httpClient; _providerManager = providerManager; } @@ -211,7 +198,7 @@ namespace MediaBrowser.Controller.Providers.TV } data.Data = GetComparisonData(imagesFileInfo); - + SetLastRefreshed(item, DateTime.UtcNow); return true; }