Pārlūkot izejas kodu

#606 - Add manual image selection for Seasons

Luke Pulverenti 11 gadi atpakaļ
vecāks
revīzija
0c93447380

+ 1 - 0
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -110,6 +110,7 @@
     <Compile Include="TV\FanArtSeasonProvider.cs" />
     <Compile Include="TV\FanArtTVProvider.cs" />
     <Compile Include="TV\FanArtTvUpdatesPrescanTask.cs" />
+    <Compile Include="TV\ManualFanartSeasonProvider.cs" />
     <Compile Include="TV\ManualTvdbEpisodeImageProvider.cs" />
     <Compile Include="TV\RemoteEpisodeProvider.cs" />
     <Compile Include="TV\RemoteSeasonProvider.cs" />

+ 2 - 5
MediaBrowser.Providers/Movies/FanArtMovieProvider.cs

@@ -201,12 +201,9 @@ namespace MediaBrowser.Providers.Movies
                     await DownloadMovieXml(movieId, cancellationToken).ConfigureAwait(false);
                 }
 
-                if (File.Exists(xmlPath))
-                {
-                    var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartMovieImageProvider.ProviderName).ConfigureAwait(false);
+                var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartMovieImageProvider.ProviderName).ConfigureAwait(false);
 
-                    await FetchImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
-                }
+                await FetchImages(item, images.ToList(), cancellationToken).ConfigureAwait(false);
             }
 
             SetLastRefreshed(item, DateTime.UtcNow);

+ 2 - 1
MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs

@@ -143,7 +143,8 @@ namespace MediaBrowser.Providers.Movies
                 images.backdrops
                 .ToList();
 
-            return eligibleBackdrops.OrderByDescending(i => i.vote_average).ThenByDescending(i => i.vote_count);
+            return eligibleBackdrops.OrderByDescending(i => i.vote_average)
+                .ThenByDescending(i => i.vote_count);
         }
 
         /// <summary>

+ 1 - 2
MediaBrowser.Providers/Movies/MovieDbProvider.cs

@@ -198,8 +198,7 @@ namespace MediaBrowser.Providers.Movies
 
         protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
         {
-            // Boxsets require two passes because we need the children to be refreshed
-            if (item is BoxSet && string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb)))
+            if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tmdb)))
             {
                 return true;
             }

+ 1 - 3
MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs

@@ -100,10 +100,8 @@ namespace MediaBrowser.Providers.Movies
 
             var timestampFileInfo = new FileInfo(timestampFile);
 
-            var refreshDays = _config.Configuration.EnableTmdbUpdates ? 1 : 7;
-
             // Don't check for tvdb updates anymore frequently than 24 hours
-            if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < refreshDays)
+            if (timestampFileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(timestampFileInfo)).TotalDays < 1)
             {
                 return;
             }

+ 4 - 13
MediaBrowser.Providers/TV/FanArtSeasonProvider.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Providers.TV
             if (!string.IsNullOrEmpty(seriesId))
             {
                 // Process images
-                var imagesXmlPath = Path.Combine(FanArtTvProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "fanart.xml");
+                var imagesXmlPath = FanArtTvProvider.Current.GetFanartXmlPath(seriesId);
 
                 var imagesFileInfo = new FileInfo(imagesXmlPath);
 
@@ -104,7 +104,7 @@ namespace MediaBrowser.Providers.TV
             if (!string.IsNullOrEmpty(seriesId))
             {
                 // Process images
-                var imagesXmlPath = Path.Combine(FanArtTvProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "fanart.xml");
+                var imagesXmlPath = FanArtTvProvider.Current.GetFanartXmlPath(seriesId);
 
                 var imagesFileInfo = new FileInfo(imagesXmlPath);
 
@@ -118,19 +118,10 @@ namespace MediaBrowser.Providers.TV
                         await FetchImages(season, xmlDoc, cancellationToken).ConfigureAwait(false);
                     }
                 }
-
-                BaseProviderInfo data;
-                if (!item.ProviderData.TryGetValue(Id, out data))
-                {
-                    data = new BaseProviderInfo();
-                    item.ProviderData[Id] = data;
-                }
-
-                SetLastRefreshed(item, DateTime.UtcNow);
-                return true;
             }
 
-            return false;
+            SetLastRefreshed(item, DateTime.UtcNow);
+            return true;
         }
 
         /// <summary>

+ 17 - 47
MediaBrowser.Providers/TV/FanArtTVProvider.cs

@@ -82,26 +82,6 @@ namespace MediaBrowser.Providers.TV
                 return false;
             }
 
-            if (!ConfigurationManager.Configuration.DownloadSeriesImages.Art &&
-                !ConfigurationManager.Configuration.DownloadSeriesImages.Logo &&
-                !ConfigurationManager.Configuration.DownloadSeriesImages.Thumb &&
-                !ConfigurationManager.Configuration.DownloadSeriesImages.Backdrops &&
-                !ConfigurationManager.Configuration.DownloadSeriesImages.Banner &&
-                !ConfigurationManager.Configuration.DownloadSeriesImages.Primary)
-            {
-                return false;
-            }
-
-            if (item.HasImage(ImageType.Primary) &&
-                item.HasImage(ImageType.Art) &&
-                item.HasImage(ImageType.Logo) &&
-                item.HasImage(ImageType.Banner) &&
-                item.HasImage(ImageType.Thumb) &&
-                item.BackdropImagePaths.Count >= ConfigurationManager.Configuration.MaxBackdrops)
-            {
-                return false;
-            }
-
             return base.NeedsRefreshInternal(item, providerInfo);
         }
 
@@ -112,28 +92,14 @@ namespace MediaBrowser.Providers.TV
             if (!string.IsNullOrEmpty(id))
             {
                 // Process images
-                var path = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, id);
+                var xmlPath = GetFanartXmlPath(id);
 
-                try
-                {
-                    var files = new DirectoryInfo(path)
-                        .EnumerateFiles("*.xml", SearchOption.TopDirectoryOnly)
-                        .Select(i => _fileSystem.GetLastWriteTimeUtc(i))
-                        .ToList();
+                var fileInfo = new FileInfo(xmlPath);
 
-                    if (files.Count > 0)
-                    {
-                        return files.Max() > providerInfo.LastRefreshed;
-                    }
-                }
-                catch (DirectoryNotFoundException)
-                {
-                    // Don't blow up
-                    return true;
-                }
+                return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
             }
-            
-            return false;
+
+            return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
         }
 
         /// <summary>
@@ -184,6 +150,12 @@ namespace MediaBrowser.Providers.TV
 
             return dataPath;
         }
+
+        public string GetFanartXmlPath(string tvdbId)
+        {
+            var dataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, tvdbId);
+            return Path.Combine(dataPath, "fanart.xml");
+        }
         
         protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
         
@@ -195,13 +167,12 @@ namespace MediaBrowser.Providers.TV
 
             if (!string.IsNullOrEmpty(seriesId))
             {
-                var seriesDataPath = GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId);
-                var xmlPath = Path.Combine(seriesDataPath, "fanart.xml");
+                var xmlPath = GetFanartXmlPath(seriesId);
 
                 // 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);
+                    await DownloadSeriesXml(seriesId, cancellationToken).ConfigureAwait(false);
                 }
 
                 if (File.Exists(xmlPath))
@@ -334,19 +305,18 @@ namespace MediaBrowser.Providers.TV
         /// <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)
+        internal async Task DownloadSeriesXml(string tvdbId, CancellationToken cancellationToken)
         {
             cancellationToken.ThrowIfCancellationRequested();
 
-            string url = string.Format(FanArtBaseUrl, ApiKey, tvdbId);
+            var url = string.Format(FanArtBaseUrl, ApiKey, tvdbId);
 
-            var xmlPath = Path.Combine(seriesDataPath, "fanart.xml");
+            var xmlPath = GetFanartXmlPath(tvdbId);
 
-            Directory.CreateDirectory(seriesDataPath);
+            Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
 
             using (var response = await HttpClient.Get(new HttpRequestOptions
             {

+ 5 - 15
MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs

@@ -86,7 +86,7 @@ namespace MediaBrowser.Providers.TV
 
                 progress.Report(5);
 
-                await UpdateSeries(seriesToUpdate, path, progress, cancellationToken).ConfigureAwait(false);
+                await UpdateSeries(seriesToUpdate, progress, cancellationToken).ConfigureAwait(false);
             }
 
             var newUpdateTime = Convert.ToInt64(DateTimeToUnixTimestamp(DateTime.UtcNow)).ToString(UsCulture);
@@ -138,18 +138,19 @@ namespace MediaBrowser.Providers.TV
         /// 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)
+        private async Task UpdateSeries(IEnumerable<string> idList, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var list = idList.ToList();
             var numComplete = 0;
 
             foreach (var id in list)
             {
-                await UpdateSeries(id, seriesDataPath, cancellationToken).ConfigureAwait(false);
+                _logger.Info("Updating series " + id);
+                
+                await FanArtTvProvider.Current.DownloadSeriesXml(id, cancellationToken).ConfigureAwait(false);
 
                 numComplete++;
                 double percent = numComplete;
@@ -160,17 +161,6 @@ namespace MediaBrowser.Providers.TV
             }
         }
 
-        private Task UpdateSeries(string tvdbId, string seriesDataPath, CancellationToken cancellationToken)
-        {
-            _logger.Info("Updating series " + tvdbId);
-
-            seriesDataPath = Path.Combine(seriesDataPath, tvdbId);
-
-            Directory.CreateDirectory(seriesDataPath);
-
-            return FanArtTvProvider.Current.DownloadSeriesXml(seriesDataPath, tvdbId, cancellationToken);
-        }
-
         /// <summary>
         /// Dates the time to unix timestamp.
         /// </summary>

+ 234 - 0
MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs

@@ -0,0 +1,234 @@
+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;
+
+namespace MediaBrowser.Providers.TV
+{
+    public class ManualFanartSeasonImageProvider : IImageProvider
+    {
+        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
+        private readonly IServerConfigurationManager _config;
+
+        public ManualFanartSeasonImageProvider(IServerConfigurationManager config)
+        {
+            _config = config;
+        }
+
+        public string Name
+        {
+            get { return ProviderName; }
+        }
+
+        public static string ProviderName
+        {
+            get { return "FanArt"; }
+        }
+
+        public bool Supports(BaseItem item)
+        {
+            return item is Season;
+        }
+
+        public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem 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(BaseItem item, CancellationToken cancellationToken)
+        {
+            var list = new List<RemoteImageInfo>();
+
+            var series = ((Season) item).Series;
+
+            if (series == null)
+            {
+                return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
+            }
+
+            var id = series.GetProviderId(MetadataProviders.Tvdb);
+
+            if (!string.IsNullOrEmpty(id) && item.IndexNumber.HasValue)
+            {
+                var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id);
+
+                try
+                {
+                    AddImages(list, item.IndexNumber.Value, xmlPath, cancellationToken);
+                }
+                catch (FileNotFoundException)
+                {
+                    // No biggie. Don't blow up
+                }
+            }
+
+            var language = _config.Configuration.PreferredMetadataLanguage;
+
+            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)
+                .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;
+                            }
+                        default:
+                            reader.Skip();
+                            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":
+                            {
+                                var url = reader.GetAttribute("url");
+                                var season = reader.GetAttribute("season");
+
+                                if (!string.IsNullOrEmpty(url) && string.Equals(season, seasonNumber.ToString(_usCulture)))
+                                {
+                                    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 Priority
+        {
+            get { return 1; }
+        }
+    }
+}