diff options
| author | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-02-03 00:35:43 -0500 |
|---|---|---|
| committer | Luke Pulverenti <luke.pulverenti@gmail.com> | 2014-02-03 00:35:43 -0500 |
| commit | 30ebfab8e0ae39213a5a550fbcbaa2463d6a74da (patch) | |
| tree | fe6614f197b623917d0034bfec02c5d500f81ab9 | |
| parent | 6261f157f340e4f544f8f41a81b3cc1f2c69f5b1 (diff) | |
converted season providers
37 files changed, 493 insertions, 860 deletions
diff --git a/MediaBrowser.Controller/Entities/BaseItem.cs b/MediaBrowser.Controller/Entities/BaseItem.cs index d9579d79f..5a041860b 100644 --- a/MediaBrowser.Controller/Entities/BaseItem.cs +++ b/MediaBrowser.Controller/Entities/BaseItem.cs @@ -784,11 +784,19 @@ namespace MediaBrowser.Controller.Entities ResetResolveArgs(); } + await BeforeRefreshMetadata(options, cancellationToken).ConfigureAwait(false); + await ProviderManager.RefreshMetadata(this, options, cancellationToken).ConfigureAwait(false); return false; } + private readonly Task _cachedTask = Task.FromResult(true); + protected virtual Task BeforeRefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken) + { + return _cachedTask; + } + [Obsolete] public virtual async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false) { diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index a4257b2a5..94db04864 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -914,11 +914,14 @@ namespace MediaBrowser.Controller.Entities return item; } - public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false) + protected override Task BeforeRefreshMetadata(MetadataRefreshOptions options, CancellationToken cancellationToken) { - var changed = await base.RefreshMetadataDirect(cancellationToken, forceSave, forceRefresh).ConfigureAwait(false); + if (SupportsShortcutChildren && LocationType == LocationType.FileSystem) + { + RefreshLinkedChildren(); + } - return (SupportsShortcutChildren && LocationType == LocationType.FileSystem && RefreshLinkedChildren()) || changed; + return base.BeforeRefreshMetadata(options, cancellationToken); } /// <summary> diff --git a/MediaBrowser.Controller/Entities/User.cs b/MediaBrowser.Controller/Entities/User.cs index c109e1d0c..5feb000af 100644 --- a/MediaBrowser.Controller/Entities/User.cs +++ b/MediaBrowser.Controller/Entities/User.cs @@ -275,33 +275,6 @@ namespace MediaBrowser.Controller.Entities } /// <summary> - /// Refresh metadata on us by execution our provider chain - /// The item will be persisted if a change is made by a provider, or if it's new or changed. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <param name="forceSave">if set to <c>true</c> [is new item].</param> - /// <param name="forceRefresh">if set to <c>true</c> [force].</param> - /// <returns>true if a provider reports we changed</returns> - public override async Task<bool> RefreshMetadataDirect(CancellationToken cancellationToken, bool forceSave = false, bool forceRefresh = false) - { - // Reload this - ResetResolveArgs(); - - var updateReason = await ProviderManager.ExecuteMetadataProviders(this, cancellationToken, forceRefresh).ConfigureAwait(false); - - var changed = updateReason.HasValue; - - if (changed || forceSave) - { - cancellationToken.ThrowIfCancellationRequested(); - - await UserManager.UpdateUser(this).ConfigureAwait(false); - } - - return changed; - } - - /// <summary> /// Updates the configuration. /// </summary> /// <param name="config">The config.</param> diff --git a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs index 391dd456e..64de1c37f 100644 --- a/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs +++ b/MediaBrowser.Providers/BoxSets/BoxSetXmlProvider.cs @@ -51,7 +51,7 @@ namespace MediaBrowser.Providers.BoxSets public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs b/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs index f2001f586..61fb791e6 100644 --- a/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs +++ b/MediaBrowser.Providers/Games/GameSystemXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Games public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/Games/GameXmlProvider.cs b/MediaBrowser.Providers/Games/GameXmlProvider.cs index fb64c2a61..e2a67de8d 100644 --- a/MediaBrowser.Providers/Games/GameXmlProvider.cs +++ b/MediaBrowser.Providers/Games/GameXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Games public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs b/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs index 096e68a8a..701cc9f85 100644 --- a/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs +++ b/MediaBrowser.Providers/LiveTv/ChannelXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.LiveTv public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/Manager/MetadataService.cs b/MediaBrowser.Providers/Manager/MetadataService.cs index f6e27238e..47345126d 100644 --- a/MediaBrowser.Providers/Manager/MetadataService.cs +++ b/MediaBrowser.Providers/Manager/MetadataService.cs @@ -89,6 +89,8 @@ namespace MediaBrowser.Providers.Manager // Next run metadata providers if (refreshOptions.MetadataRefreshMode != MetadataRefreshMode.None) { + updateType = updateType | BeforeMetadataRefresh(itemOfType); + var providers = GetProviders(item, lastResult.DateLastMetadataRefresh.HasValue, refreshOptions).ToList(); if (providers.Count > 0) @@ -100,6 +102,8 @@ namespace MediaBrowser.Providers.Manager refreshResult.SetDateLastMetadataRefresh(DateTime.UtcNow); refreshResult.AddImageProvidersRefreshed(result.Providers); } + + updateType = updateType | AfterMetadataRefresh(itemOfType); } // Next run remote image providers, but only if local image providers didn't throw an exception @@ -116,8 +120,6 @@ namespace MediaBrowser.Providers.Manager refreshResult.SetDateLastImagesRefresh(DateTime.UtcNow); refreshResult.AddImageProvidersRefreshed(result.Providers); } - - updateType = updateType | AfterMetadataRefresh(itemOfType); } var providersHadChanges = updateType > ItemUpdateType.Unspecified; @@ -158,6 +160,16 @@ namespace MediaBrowser.Providers.Manager } /// <summary> + /// Befores the metadata refresh. + /// </summary> + /// <param name="item">The item.</param> + /// <returns>ItemUpdateType.</returns> + protected virtual ItemUpdateType BeforeMetadataRefresh(TItemType item) + { + return ItemUpdateType.Unspecified; + } + + /// <summary> /// Gets the providers. /// </summary> /// <param name="item">The item.</param> @@ -261,12 +273,17 @@ namespace MediaBrowser.Providers.Manager if (localItem.HasMetadata) { - MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true); - refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; + if (!string.IsNullOrEmpty(localItem.Item.Name)) + { + MergeData(localItem.Item, temp, new List<MetadataFields>(), !options.ReplaceAllMetadata, true); + refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport; + + // Only one local provider allowed per item + hasLocalMetadata = true; + break; + } - // Only one local provider allowed per item - hasLocalMetadata = true; - break; + Logger.Error("Invalid local metadata found for: " + item.Path); } } catch (OperationCanceledException) diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9170e7268..282facfc8 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -65,6 +65,7 @@ </ItemGroup> <ItemGroup> <Compile Include="All\LocalImageProvider.cs" /> + <Compile Include="Books\BookMetadataService.cs" /> <Compile Include="BoxSets\BoxSetMetadataService.cs" /> <Compile Include="BoxSets\MovieDbBoxSetImageProvider.cs" /> <Compile Include="BoxSets\MovieDbBoxSetProvider.cs" /> @@ -151,26 +152,24 @@ <Compile Include="TV\EpisodeIndexNumberProvider.cs" /> <Compile Include="TV\EpisodeProviderFromXml.cs" /> <Compile Include="TV\EpisodeXmlParser.cs" /> - <Compile Include="TV\FanArtSeasonProvider.cs" /> <Compile Include="TV\FanArtTVProvider.cs" /> <Compile Include="TV\FanArtTvUpdatesPrescanTask.cs" /> - <Compile Include="TV\ManualFanartSeasonProvider.cs" /> + <Compile Include="TV\FanartSeasonProvider.cs" /> <Compile Include="TV\ManualFanartSeriesProvider.cs" /> <Compile Include="TV\ManualTvdbEpisodeImageProvider.cs" /> <Compile Include="People\TvdbPersonImageProvider.cs" /> - <Compile Include="TV\ManualTvdbSeasonImageProvider.cs" /> + <Compile Include="TV\TvdbSeasonImageProvider.cs" /> <Compile Include="TV\ManualTvdbSeriesImageProvider.cs" /> - <Compile Include="TV\SeasonIndexNumberProvider.cs" /> + <Compile Include="TV\SeasonMetadataService.cs" /> <Compile Include="TV\TvdbEpisodeProvider.cs" /> - <Compile Include="TV\TvdbSeasonProvider.cs" /> + <Compile Include="TV\TvdbSeriesImageProvider.cs" /> <Compile Include="TV\TvdbSeriesProvider.cs" /> - <Compile Include="TV\SeasonProviderFromXml.cs" /> + <Compile Include="TV\SeasonXmlProvider.cs" /> <Compile Include="TV\SeriesDynamicInfoProvider.cs" /> <Compile Include="TV\SeriesPostScanTask.cs" /> <Compile Include="TV\SeriesProviderFromXml.cs" /> <Compile Include="TV\SeriesXmlParser.cs" /> <Compile Include="TV\TvdbPrescanTask.cs" /> - <Compile Include="TV\TvdbSeriesImageProvider.cs" /> <Compile Include="UserRootFolderNameProvider.cs" /> <Compile Include="Users\UserMetadataService.cs" /> <Compile Include="VirtualItemImageValidator.cs" /> diff --git a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs index e0d830369..7c7de6182 100644 --- a/MediaBrowser.Providers/Music/AlbumXmlProvider.cs +++ b/MediaBrowser.Providers/Music/AlbumXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Music public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs b/MediaBrowser.Providers/Music/ArtistXmlProvider.cs index 3f073d512..30c38bdaa 100644 --- a/MediaBrowser.Providers/Music/ArtistXmlProvider.cs +++ b/MediaBrowser.Providers/Music/ArtistXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Music public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/People/PersonXmlProvider.cs b/MediaBrowser.Providers/People/PersonXmlProvider.cs index 72aef7a4c..c23458f68 100644 --- a/MediaBrowser.Providers/People/PersonXmlProvider.cs +++ b/MediaBrowser.Providers/People/PersonXmlProvider.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.People public string Name { - get { return "Media Browser xml"; } + get { return "Media Browser Xml"; } } protected override FileInfo GetXmlFile(string path) diff --git a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs index 34f91ba28..bc51a5286 100644 --- a/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/AlbumXmlSaver.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs index 5c40f3b38..1ae1eaa64 100644 --- a/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/ArtistXmlSaver.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs index 35c53115d..06a17528b 100644 --- a/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/BoxSetXmlSaver.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs b/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs index 3d8cbb415..0c857c5ec 100644 --- a/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/ChannelXmlSaver.cs @@ -37,7 +37,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs index c1f6bb7de..e60acaa38 100644 --- a/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/EpisodeXmlSaver.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs index fe8eec3a5..e0ae638e9 100644 --- a/MediaBrowser.Providers/Savers/FolderXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/FolderXmlSaver.cs @@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs b/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs index 38af31923..017f17f8d 100644 --- a/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameSystemXmlSaver.cs @@ -23,7 +23,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/GameXmlSaver.cs b/MediaBrowser.Providers/Savers/GameXmlSaver.cs index b9abb3773..a6225b58c 100644 --- a/MediaBrowser.Providers/Savers/GameXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/GameXmlSaver.cs @@ -28,7 +28,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs index 2c19cb628..e5ba1aefd 100644 --- a/MediaBrowser.Providers/Savers/MovieXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/MovieXmlSaver.cs @@ -32,7 +32,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs index 12703aa3c..167e514a8 100644 --- a/MediaBrowser.Providers/Savers/PersonXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/PersonXmlSaver.cs @@ -18,7 +18,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs index 6c4d3fb19..5773fc1de 100644 --- a/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeasonXmlSaver.cs @@ -22,7 +22,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs index 21019fa0e..b76167868 100644 --- a/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs +++ b/MediaBrowser.Providers/Savers/SeriesXmlSaver.cs @@ -24,7 +24,7 @@ namespace MediaBrowser.Providers.Savers { get { - return "Media Browser xml"; + return "Media Browser Xml"; } } diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs index 50db33b3d..60643252b 100644 --- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs @@ -1,190 +1,303 @@ using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; using MediaBrowser.Model.Providers; +using MediaBrowser.Providers.Music; 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.Model.Net; -using System.Net; -using MediaBrowser.Providers.Music; +using System.Xml; namespace MediaBrowser.Providers.TV { - /// <summary> - /// Class FanArtSeasonProvider - /// </summary> - class FanArtSeasonProvider : BaseMetadataProvider + public class FanartSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; + private readonly CultureInfo _usCulture = new CultureInfo("en-US"); + private readonly IServerConfigurationManager _config; + private readonly IHttpClient _httpClient; private readonly IFileSystem _fileSystem; - /// <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, IFileSystem fileSystem) - : base(logManager, configurationManager) + public FanartSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) { - _providerManager = providerManager; + _config = config; + _httpClient = httpClient; _fileSystem = fileSystem; } - public override ItemUpdateType ItemUpdateType + public string Name { - get - { - return ItemUpdateType.ImageUpdate; - } + get { return ProviderName; } } - - /// <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) + + public static string ProviderName + { + get { return "FanArt"; } + } + + public bool Supports(IHasImages item) { return item is Season; } - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority + public IEnumerable<ImageType> GetSupportedImages(IHasImages item) { - get { return MetadataProviderPriority.Third; } + return new List<ImageType> + { + ImageType.Backdrop, + ImageType.Thumb + }; } - protected override DateTime CompareDate(BaseItem item) + public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) { + var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); + + return images.Where(i => i.Type == imageType); + } + + public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) + { + var list = new List<RemoteImageInfo>(); + var season = (Season)item; - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; + var series = season.Series; - if (!string.IsNullOrEmpty(seriesId)) + if (series != null) { - // Process images - var imagesXmlPath = FanArtTvProvider.Current.GetFanartXmlPath(seriesId); + var id = series.GetProviderId(MetadataProviders.Tvdb); - var imagesFileInfo = new FileInfo(imagesXmlPath); - - if (imagesFileInfo.Exists) + if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue) { - return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo); + await FanArtTvProvider.Current.EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false); + + var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id); + + try + { + AddImages(list, season.IndexNumber.Value, xmlPath, cancellationToken); + } + catch (FileNotFoundException) + { + // No biggie. Don't blow up + } } } - return base.CompareDate(item); + var language = item.GetPreferredMetadataLanguage(); + + var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); + + // Sort first by width to prioritize HD versions + return list.OrderByDescending(i => i.Width ?? 0) + .ThenByDescending(i => + { + if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 3; + } + if (!isLanguageEn) + { + if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) + { + return 2; + } + } + if (string.IsNullOrEmpty(i.Language)) + { + return isLanguageEn ? 3 : 2; + } + return 0; + }) + .ThenByDescending(i => i.CommunityRating ?? 0) + .ThenByDescending(i => i.VoteCount ?? 0); } - /// <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, BaseProviderInfo providerInfo, CancellationToken cancellationToken) + private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string xmlPath, CancellationToken cancellationToken) { - cancellationToken.ThrowIfCancellationRequested(); + using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) + { + // Use XmlReader for best performance + using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings + { + CheckCharacters = false, + IgnoreProcessingInstructions = true, + IgnoreComments = true, + ValidationType = ValidationType.None + })) + { + reader.MoveToContent(); - var season = (Season) item; + // Loop through each element + while (reader.Read()) + { + cancellationToken.ThrowIfCancellationRequested(); - // Process images - var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartSeasonImageProvider.ProviderName).ConfigureAwait(false); - await FetchImages(season, images.ToList(), cancellationToken).ConfigureAwait(false); + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "series": + { + using (var subReader = reader.ReadSubtree()) + { + AddImages(list, subReader, seasonNumber, cancellationToken); + } + break; + } - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return true; + default: + reader.Skip(); + break; + } + } + } + } + } } - /// <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, List<RemoteImageInfo> images, CancellationToken cancellationToken) + private void AddImages(List<RemoteImageInfo> list, XmlReader reader, int seasonNumber, CancellationToken cancellationToken) { - var options = ConfigurationManager.Configuration.GetMetadataOptions("Season") ?? new MetadataOptions(); + reader.MoveToContent(); - if (options.IsEnabled(ImageType.Thumb) && !season.HasImage(ImageType.Thumb) && !season.LockedFields.Contains(MetadataFields.Images)) + while (reader.Read()) { - await SaveImage(season, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false); + if (reader.NodeType == XmlNodeType.Element) + { + switch (reader.Name) + { + case "seasonthumbs": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281, seasonNumber); + } + break; + } + case "showbackgrounds": + { + using (var subReader = reader.ReadSubtree()) + { + PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080, seasonNumber); + } + break; + } + default: + { + using (reader.ReadSubtree()) + { + } + break; + } + } + } } } - private async Task SaveImage(BaseItem item, List<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken) + private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height, int seasonNumber) { - foreach (var image in images.Where(i => i.Type == type)) + reader.MoveToContent(); + + while (reader.Read()) { - try - { - await _providerManager.SaveImage(item, image.Url, FanartArtistProvider.FanArtResourcePool, type, null, cancellationToken).ConfigureAwait(false); - break; - } - catch (HttpException ex) + cancellationToken.ThrowIfCancellationRequested(); + + if (reader.NodeType == XmlNodeType.Element) { - // Sometimes fanart has bad url's in their xml - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) + switch (reader.Name) { - continue; + case "seasonthumb": + case "showbackground": + { + var url = reader.GetAttribute("url"); + var season = reader.GetAttribute("season"); + + int imageSeasonNumber; + + if (!string.IsNullOrEmpty(url) && + !string.IsNullOrEmpty(season) && + int.TryParse(season, NumberStyles.Any, _usCulture, out imageSeasonNumber) && + seasonNumber == imageSeasonNumber) + { + var likesString = reader.GetAttribute("likes"); + int likes; + + var info = new RemoteImageInfo + { + RatingType = RatingType.Likes, + Type = type, + Width = width, + Height = height, + ProviderName = Name, + Url = url, + Language = reader.GetAttribute("lang") + }; + + if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes)) + { + info.CommunityRating = likes; + } + + list.Add(info); + } + + break; + } + default: + reader.Skip(); + break; } - break; } } } - /// <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 + public int Order { - get - { - return true; - } + get { return 1; } } - /// <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 + public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) { - get + return _httpClient.GetResponse(new HttpRequestOptions { - return true; - } + CancellationToken = cancellationToken, + Url = url, + ResourcePool = FanartArtistProvider.FanArtResourcePool + }); } - /// <summary> - /// Gets the provider version. - /// </summary> - /// <value>The provider version.</value> - protected override string ProviderVersion + public bool HasChanged(IHasMetadata item, DateTime date) { - get + var season = (Season)item; + var series = season.Series; + + if (series == null) { - return "3"; + return false; } + + var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); + + if (!String.IsNullOrEmpty(tvdbId)) + { + // Process images + var imagesXmlPath = FanArtTvProvider.Current.GetFanartXmlPath(tvdbId); + + var fileInfo = new FileInfo(imagesXmlPath); + + return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + } + + return false; } } } diff --git a/MediaBrowser.Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Providers/TV/FanArtTVProvider.cs index 3f2199fc4..baadf350f 100644 --- a/MediaBrowser.Providers/TV/FanArtTVProvider.cs +++ b/MediaBrowser.Providers/TV/FanArtTVProvider.cs @@ -72,7 +72,7 @@ namespace MediaBrowser.Providers.TV return ItemUpdateType.ImageUpdate; } } - + /// <summary> /// Needses the refresh internal. /// </summary> @@ -160,7 +160,7 @@ namespace MediaBrowser.Providers.TV var dataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, tvdbId); return Path.Combine(dataPath, "fanart.xml"); } - + protected readonly CultureInfo UsCulture = new CultureInfo("en-US"); public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) @@ -199,7 +199,7 @@ namespace MediaBrowser.Providers.TV private async Task FetchFromXml(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken) { var options = ConfigurationManager.Configuration.GetMetadataOptions("Series") ?? new MetadataOptions(); - + if (!item.LockedFields.Contains(MetadataFields.Images)) { cancellationToken.ThrowIfCancellationRequested(); @@ -278,6 +278,23 @@ namespace MediaBrowser.Providers.TV } } + internal Task EnsureSeriesXml(string tvdbId, CancellationToken cancellationToken) + { + var xmlPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, tvdbId); + + var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath); + + if (fileInfo.Exists) + { + if (ConfigurationManager.Configuration.EnableFanArtUpdates || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7) + { + return Task.FromResult(true); + } + } + + return DownloadSeriesXml(tvdbId, cancellationToken); + } + /// <summary> /// Downloads the series XML. /// </summary> diff --git a/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs b/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs deleted file mode 100644 index c7b2f595b..000000000 --- a/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs +++ /dev/null @@ -1,276 +0,0 @@ -using MediaBrowser.Common.Net; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Dto; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Providers; -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using System.Xml; -using MediaBrowser.Providers.Music; - -namespace MediaBrowser.Providers.TV -{ - public class ManualFanartSeasonImageProvider : IRemoteImageProvider, IHasOrder - { - private readonly CultureInfo _usCulture = new CultureInfo("en-US"); - private readonly IServerConfigurationManager _config; - private readonly IHttpClient _httpClient; - - public ManualFanartSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient) - { - _config = config; - _httpClient = httpClient; - } - - public string Name - { - get { return ProviderName; } - } - - public static string ProviderName - { - get { return "FanArt"; } - } - - public bool Supports(IHasImages item) - { - return item is Season; - } - - public IEnumerable<ImageType> GetSupportedImages(IHasImages item) - { - return new List<ImageType> - { - ImageType.Backdrop, - ImageType.Thumb - }; - } - - public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken) - { - var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false); - - return images.Where(i => i.Type == imageType); - } - - public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) - { - var list = new List<RemoteImageInfo>(); - - var season = (Season)item; - var series = season.Series; - - if (series != null) - { - var id = series.GetProviderId(MetadataProviders.Tvdb); - - if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue) - { - var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id); - - try - { - AddImages(list, season.IndexNumber.Value, xmlPath, cancellationToken); - } - catch (FileNotFoundException) - { - // No biggie. Don't blow up - } - } - } - - var language = item.GetPreferredMetadataLanguage(); - - var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase); - - // Sort first by width to prioritize HD versions - list = list.OrderByDescending(i => i.Width ?? 0) - .ThenByDescending(i => - { - if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 3; - } - if (!isLanguageEn) - { - if (string.Equals("en", i.Language, StringComparison.OrdinalIgnoreCase)) - { - return 2; - } - } - if (string.IsNullOrEmpty(i.Language)) - { - return isLanguageEn ? 3 : 2; - } - return 0; - }) - .ThenByDescending(i => i.CommunityRating ?? 0) - .ThenByDescending(i => i.VoteCount ?? 0) - .ToList(); - - return Task.FromResult<IEnumerable<RemoteImageInfo>>(list); - } - - private void AddImages(List<RemoteImageInfo> list, int seasonNumber, string xmlPath, CancellationToken cancellationToken) - { - using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8)) - { - // Use XmlReader for best performance - using (var reader = XmlReader.Create(streamReader, new XmlReaderSettings - { - CheckCharacters = false, - IgnoreProcessingInstructions = true, - IgnoreComments = true, - ValidationType = ValidationType.None - })) - { - reader.MoveToContent(); - - // Loop through each element - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "series": - { - using (var subReader = reader.ReadSubtree()) - { - AddImages(list, subReader, seasonNumber, cancellationToken); - } - break; - } - - default: - reader.Skip(); - break; - } - } - } - } - } - } - - private void AddImages(List<RemoteImageInfo> list, XmlReader reader, int seasonNumber, CancellationToken cancellationToken) - { - reader.MoveToContent(); - - while (reader.Read()) - { - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "seasonthumbs": - { - using (var subReader = reader.ReadSubtree()) - { - PopulateImageCategory(list, subReader, cancellationToken, ImageType.Thumb, 500, 281, seasonNumber); - } - break; - } - case "showbackgrounds": - { - using (var subReader = reader.ReadSubtree()) - { - PopulateImageCategory(list, subReader, cancellationToken, ImageType.Backdrop, 1920, 1080, seasonNumber); - } - break; - } - default: - { - using (reader.ReadSubtree()) - { - } - break; - } - } - } - } - } - - private void PopulateImageCategory(List<RemoteImageInfo> list, XmlReader reader, CancellationToken cancellationToken, ImageType type, int width, int height, int seasonNumber) - { - reader.MoveToContent(); - - while (reader.Read()) - { - cancellationToken.ThrowIfCancellationRequested(); - - if (reader.NodeType == XmlNodeType.Element) - { - switch (reader.Name) - { - case "seasonthumb": - case "showbackground": - { - var url = reader.GetAttribute("url"); - var season = reader.GetAttribute("season"); - - int imageSeasonNumber; - - if (!string.IsNullOrEmpty(url) && - !string.IsNullOrEmpty(season) && - int.TryParse(season, NumberStyles.Any, _usCulture, out imageSeasonNumber) && - seasonNumber == imageSeasonNumber) - { - var likesString = reader.GetAttribute("likes"); - int likes; - - var info = new RemoteImageInfo - { - RatingType = RatingType.Likes, - Type = type, - Width = width, - Height = height, - ProviderName = Name, - Url = url, - Language = reader.GetAttribute("lang") - }; - - if (!string.IsNullOrEmpty(likesString) && int.TryParse(likesString, NumberStyles.Any, _usCulture, out likes)) - { - info.CommunityRating = likes; - } - - list.Add(info); - } - - break; - } - default: - reader.Skip(); - break; - } - } - } - } - - public int Order - { - get { return 1; } - } - - public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken) - { - return _httpClient.GetResponse(new HttpRequestOptions - { - CancellationToken = cancellationToken, - Url = url, - ResourcePool = FanartArtistProvider.FanArtResourcePool - }); - } - } -} diff --git a/MediaBrowser.Providers/TV/SeasonIndexNumberProvider.cs b/MediaBrowser.Providers/TV/SeasonIndexNumberProvider.cs deleted file mode 100644 index 593784201..000000000 --- a/MediaBrowser.Providers/TV/SeasonIndexNumberProvider.cs +++ /dev/null @@ -1,83 +0,0 @@ -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.TV -{ - class SeasonIndexNumberProvider : BaseMetadataProvider - { - /// <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> - public SeasonIndexNumberProvider(ILogManager logManager, IServerConfigurationManager configurationManager) - : base(logManager, configurationManager) - { - } - - protected override bool RefreshOnVersionChange - { - get - { - return true; - } - } - - protected override string ProviderVersion - { - get - { - return "2"; - } - } - - /// <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) - { - if (item is Season) - { - var locationType = item.LocationType; - return locationType != LocationType.Virtual && locationType != LocationType.Remote; - } - return false; - } - - /// <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="providerInfo">The provider information.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task{System.Boolean}.</returns> - public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - item.IndexNumber = TVUtils.GetSeasonNumberFromPath(item.Path); - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - - return TrueTaskResult; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.First; } - } - } -} diff --git a/MediaBrowser.Providers/TV/SeasonMetadataService.cs b/MediaBrowser.Providers/TV/SeasonMetadataService.cs new file mode 100644 index 000000000..da276221b --- /dev/null +++ b/MediaBrowser.Providers/TV/SeasonMetadataService.cs @@ -0,0 +1,58 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Configuration; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Library; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Entities; +using MediaBrowser.Model.Logging; +using MediaBrowser.Providers.Manager; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.TV +{ + public class SeasonMetadataService : MetadataService<Season, ItemId> + { + private readonly ILibraryManager _libraryManager; + + public SeasonMetadataService(IServerConfigurationManager serverConfigurationManager, ILogger logger, IProviderManager providerManager, IProviderRepository providerRepo, IFileSystem fileSystem, ILibraryManager libraryManager) + : base(serverConfigurationManager, logger, providerManager, providerRepo, fileSystem) + { + _libraryManager = libraryManager; + } + + /// <summary> + /// Merges the specified source. + /// </summary> + /// <param name="source">The source.</param> + /// <param name="target">The target.</param> + /// <param name="lockedFields">The locked fields.</param> + /// <param name="replaceData">if set to <c>true</c> [replace data].</param> + /// <param name="mergeMetadataSettings">if set to <c>true</c> [merge metadata settings].</param> + protected override void MergeData(Season source, Season target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings) + { + ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings); + } + + protected override Task SaveItem(Season item, ItemUpdateType reason, CancellationToken cancellationToken) + { + return _libraryManager.UpdateItem(item, reason, cancellationToken); + } + + protected override ItemUpdateType BeforeMetadataRefresh(Season item) + { + var updateType = base.BeforeMetadataRefresh(item); + + var currentIndexNumber = item.IndexNumber; + + item.IndexNumber = item.IndexNumber ?? TVUtils.GetSeasonNumberFromPath(item.Path); + + if ((currentIndexNumber ?? -1) != (item.IndexNumber ?? -1)) + { + updateType = updateType | ItemUpdateType.MetadataImport; + } + return updateType; + } + } +} diff --git a/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs b/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs deleted file mode 100644 index 9fbcad7c0..000000000 --- a/MediaBrowser.Providers/TV/SeasonProviderFromXml.cs +++ /dev/null @@ -1,97 +0,0 @@ -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.IO; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.TV -{ - /// <summary> - /// Class SeriesProviderFromXml - /// </summary> - public class SeasonProviderFromXml : BaseMetadataProvider - { - private readonly IFileSystem _fileSystem; - - public SeasonProviderFromXml(ILogManager logManager, IServerConfigurationManager configurationManager, IFileSystem fileSystem) - : base(logManager, configurationManager) - { - _fileSystem = fileSystem; - } - - /// <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 && item.LocationType == LocationType.FileSystem; - } - - /// <summary> - /// Gets the priority. - /// </summary> - /// <value>The priority.</value> - public override MetadataProviderPriority Priority - { - get { return MetadataProviderPriority.Second; } - } - - private const string XmlFileName = "season.xml"; - protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo) - { - var xml = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName)); - - if (xml == null) - { - return false; - } - - return _fileSystem.GetLastWriteTimeUtc(xml) > item.DateLastSaved; - } - - /// <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, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var metadataFile = item.ResolveArgs.GetMetaFileByPath(Path.Combine(item.MetaLocation, XmlFileName)); - - if (metadataFile != null) - { - var path = metadataFile.FullName; - - await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); - - try - { - new BaseItemXmlParser<Season>(Logger).Fetch((Season)item, path, cancellationToken); - } - finally - { - XmlParsingResourcePool.Release(); - } - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - - return true; - } - - return false; - } - } -} diff --git a/MediaBrowser.Providers/TV/SeasonXmlProvider.cs b/MediaBrowser.Providers/TV/SeasonXmlProvider.cs new file mode 100644 index 000000000..9dcc9fe4f --- /dev/null +++ b/MediaBrowser.Providers/TV/SeasonXmlProvider.cs @@ -0,0 +1,63 @@ +using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.TV; +using MediaBrowser.Controller.Providers; +using MediaBrowser.Model.Logging; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MediaBrowser.Providers.TV +{ + /// <summary> + /// Class SeriesProviderFromXml + /// </summary> + public class SeasonXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Season> + { + private readonly ILogger _logger; + + public SeasonXmlProvider(IFileSystem fileSystem, ILogger logger) + : base(fileSystem) + { + _logger = logger; + } + + public async Task<MetadataResult<Season>> GetMetadata(string path, CancellationToken cancellationToken) + { + path = GetXmlFile(path).FullName; + + var result = new MetadataResult<Season>(); + + await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false); + + try + { + var person = new Season(); + + new BaseItemXmlParser<Season>(_logger).Fetch(person, path, cancellationToken); + result.HasMetadata = true; + result.Item = person; + } + catch (FileNotFoundException) + { + result.HasMetadata = false; + } + finally + { + XmlParsingResourcePool.Release(); + } + + return result; + } + + public string Name + { + get { return "Media Browser Xml"; } + } + + protected override FileInfo GetXmlFile(string path) + { + return new FileInfo(Path.Combine(path, "season.xml")); + } + } +} + diff --git a/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs index 458482bf6..a63f3ee24 100644 --- a/MediaBrowser.Providers/TV/ManualTvdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeasonImageProvider.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Common.Net; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.Net; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.TV; @@ -19,16 +20,18 @@ using System.Xml; namespace MediaBrowser.Providers.TV { - public class ManualTvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder + public class TvdbSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor { private readonly IServerConfigurationManager _config; private readonly CultureInfo _usCulture = new CultureInfo("en-US"); private readonly IHttpClient _httpClient; + private readonly IFileSystem _fileSystem; - public ManualTvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient) + public TvdbSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem) { _config = config; _httpClient = httpClient; + _fileSystem = fileSystem; } public string Name @@ -63,14 +66,17 @@ namespace MediaBrowser.Providers.TV return images.Where(i => i.Type == imageType); } - public Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) + public async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken) { var season = (Season)item; + var series = season.Series; - var seriesId = season.Series != null ? season.Series.GetProviderId(MetadataProviders.Tvdb) : null; + var seriesId = series != null ? series.GetProviderId(MetadataProviders.Tvdb) : null; if (!string.IsNullOrEmpty(seriesId) && season.IndexNumber.HasValue) { + await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesId, series.GetPreferredMetadataLanguage(), cancellationToken).ConfigureAwait(false); + // Process images var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId); @@ -78,9 +84,7 @@ namespace MediaBrowser.Providers.TV try { - var result = GetImages(path, item.GetPreferredMetadataLanguage(), season.IndexNumber.Value, cancellationToken); - - return Task.FromResult(result); + return GetImages(path, item.GetPreferredMetadataLanguage(), season.IndexNumber.Value, cancellationToken); } catch (FileNotFoundException) { @@ -88,7 +92,7 @@ namespace MediaBrowser.Providers.TV } } - return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { }); + return new RemoteImageInfo[] { }; } private IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, int seasonNumber, CancellationToken cancellationToken) @@ -335,5 +339,30 @@ namespace MediaBrowser.Providers.TV ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool }); } + + public bool HasChanged(IHasMetadata item, DateTime date) + { + var season = (Season)item; + var series = season.Series; + + if (series == null) + { + return false; + } + + var tvdbId = series.GetProviderId(MetadataProviders.Tvdb); + + if (!String.IsNullOrEmpty(tvdbId)) + { + // Process images + var imagesXmlPath = Path.Combine(TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, tvdbId), "banners.xml"); + + var fileInfo = new FileInfo(imagesXmlPath); + + return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > date; + } + + return false; + } } } diff --git a/MediaBrowser.Providers/TV/TvdbSeasonProvider.cs b/MediaBrowser.Providers/TV/TvdbSeasonProvider.cs deleted file mode 100644 index d7281ac21..000000000 --- a/MediaBrowser.Providers/TV/TvdbSeasonProvider.cs +++ /dev/null @@ -1,211 +0,0 @@ -using System.Net; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Configuration; -using MediaBrowser.Controller.Entities; -using MediaBrowser.Controller.Entities.TV; -using MediaBrowser.Controller.Library; -using MediaBrowser.Controller.Providers; -using MediaBrowser.Model.Configuration; -using MediaBrowser.Model.Entities; -using MediaBrowser.Model.Logging; -using MediaBrowser.Model.Net; -using MediaBrowser.Model.Providers; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace MediaBrowser.Providers.TV -{ - /// <summary> - /// Class RemoteSeasonProvider - /// </summary> - class TvdbSeasonProvider : BaseMetadataProvider - { - /// <summary> - /// The _provider manager - /// </summary> - private readonly IProviderManager _providerManager; - private readonly IFileSystem _fileSystem; - - /// <summary> - /// Initializes a new instance of the <see cref="TvdbSeasonProvider"/> 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 TvdbSeasonProvider(ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem) - : base(logManager, configurationManager) - { - _providerManager = providerManager; - _fileSystem = fileSystem; - } - - /// <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; - } - } - - public override ItemUpdateType ItemUpdateType - { - get - { - return ItemUpdateType.ImageUpdate; - } - } - - /// <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 "2"; - } - } - - protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo) - { - 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(TvdbSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml"); - - var imagesFileInfo = new FileInfo(imagesXmlPath); - - if (imagesFileInfo.Exists) - { - return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo) > providerInfo.LastRefreshed; - } - } - return false; - } - - protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) - { - if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Banner) && 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, BaseProviderInfo providerInfo, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); - - var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualTvdbSeasonImageProvider.ProviderName).ConfigureAwait(false); - - await DownloadImages(item, images.ToList(), cancellationToken).ConfigureAwait(false); - - SetLastRefreshed(item, DateTime.UtcNow, providerInfo); - return true; - } - - private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, CancellationToken cancellationToken) - { - var options = ConfigurationManager.Configuration.GetMetadataOptions("Season") ?? new MetadataOptions(); - var backdropLimit = options.GetLimit(ImageType.Backdrop); - - if (!item.LockedFields.Contains(MetadataFields.Images)) - { - if (!item.HasImage(ImageType.Primary)) - { - await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false); - } - - if (options.IsEnabled(ImageType.Banner) && !item.HasImage(ImageType.Banner)) - { - await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false); - } - } - - if (options.IsEnabled(ImageType.Backdrop) && item.BackdropImagePaths.Count < backdropLimit && !item.LockedFields.Contains(MetadataFields.Backdrops)) - { - foreach (var backdrop in images.Where(i => i.Type == ImageType.Backdrop)) - { - var url = backdrop.Url; - - await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, null, cancellationToken).ConfigureAwait(false); - - if (item.BackdropImagePaths.Count >= backdropLimit) break; - } - } - } - - private async Task SaveImage(BaseItem item, List<RemoteImageInfo> images, ImageType type, CancellationToken cancellationToken) - { - foreach (var image in images.Where(i => i.Type == type)) - { - try - { - await _providerManager.SaveImage(item, image.Url, TvdbSeriesProvider.Current.TvDbResourcePool, type, null, cancellationToken).ConfigureAwait(false); - break; - } - catch (HttpException ex) - { - // Sometimes fanart has bad url's in their xml - if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound) - { - continue; - } - break; - } - } - } - } -} diff --git a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs index 4df391e2a..8d7ef5af9 100644 --- a/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TvdbSeriesProvider.cs @@ -324,6 +324,26 @@ namespace MediaBrowser.Providers.TV await ExtractEpisodes(seriesDataPath, Path.Combine(seriesDataPath, preferredMetadataLanguage + ".xml"), lastTvDbUpdateTime).ConfigureAwait(false); } + internal async Task EnsureSeriesInfo(string seriesId, string preferredMetadataLanguage, CancellationToken cancellationToken) + { + var seriesDataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId); + + Directory.CreateDirectory(seriesDataPath); + + var files = Directory.EnumerateFiles(seriesDataPath, "*.xml", SearchOption.TopDirectoryOnly) + .Select(Path.GetFileName) + .ToList(); + + var seriesXmlFilename = preferredMetadataLanguage + ".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, null, preferredMetadataLanguage, cancellationToken).ConfigureAwait(false); + } + } + private void DeleteXmlFiles(string path) { try diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index daf905a8a..d38e020fa 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common.Internal</id> - <version>3.0.317</version> + <version>3.0.318</version> <title>MediaBrowser.Common.Internal</title> <authors>Luke</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.317" /> + <dependency id="MediaBrowser.Common" version="3.0.318" /> <dependency id="NLog" version="2.1.0" /> <dependency id="SimpleInjector" version="2.4.1" /> <dependency id="sharpcompress" version="0.10.2" /> diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 4a3c25880..5ad30a09f 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd"> <metadata> <id>MediaBrowser.Common</id> - <version>3.0.317</version> + <version>3.0.318</version> <title>MediaBrowser.Common</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 08f0a1188..a24d6c3ac 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd"> <metadata> <id>MediaBrowser.Server.Core</id> - <version>3.0.317</version> + <version>3.0.318</version> <title>Media Browser.Server.Core</title> <authors>Media Browser Team</authors> <owners>ebr,Luke,scottisafool</owners> @@ -12,7 +12,7 @@ <description>Contains core components required to build plugins for Media Browser Server.</description> <copyright>Copyright © Media Browser 2013</copyright> <dependencies> - <dependency id="MediaBrowser.Common" version="3.0.317" /> + <dependency id="MediaBrowser.Common" version="3.0.318" /> </dependencies> </metadata> <files> |
