diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 6d0ab05f8e..ef1aa67778 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -110,6 +110,7 @@
+
diff --git a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs b/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
index 30fb8c659b..24f17556b4 100644
--- a/MediaBrowser.Providers/Movies/FanArtMovieProvider.cs
+++ b/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);
diff --git a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs b/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs
index 4ae15e91f4..7c6ede0c23 100644
--- a/MediaBrowser.Providers/Movies/ManualMovieDbImageProvider.cs
+++ b/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);
}
///
diff --git a/MediaBrowser.Providers/Movies/MovieDbProvider.cs b/MediaBrowser.Providers/Movies/MovieDbProvider.cs
index 67cec7498e..baec90c66a 100644
--- a/MediaBrowser.Providers/Movies/MovieDbProvider.cs
+++ b/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;
}
diff --git a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs b/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs
index 4c1838cfc5..50e04a9afd 100644
--- a/MediaBrowser.Providers/Movies/MovieUpdatesPrescanTask.cs
+++ b/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;
}
diff --git a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs b/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
index fe316e85b7..3d1d3f8b92 100644
--- a/MediaBrowser.Providers/TV/FanArtSeasonProvider.cs
+++ b/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;
}
///
diff --git a/MediaBrowser.Providers/TV/FanArtTVProvider.cs b/MediaBrowser.Providers/TV/FanArtTVProvider.cs
index af89bc205e..e349b82ce1 100644
--- a/MediaBrowser.Providers/TV/FanArtTVProvider.cs
+++ b/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);
}
///
@@ -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
///
/// Downloads the series XML.
///
- /// The series data path.
/// The TVDB id.
/// The cancellation token.
/// Task.
- 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
{
diff --git a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs b/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs
index 5c1c7a69d9..4bc7c3c4f5 100644
--- a/MediaBrowser.Providers/TV/FanArtTvUpdatesPrescanTask.cs
+++ b/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.
///
/// The id list.
- /// The artists data path.
/// The progress.
/// The cancellation token.
/// Task.
- private async Task UpdateSeries(IEnumerable idList, string seriesDataPath, IProgress progress, CancellationToken cancellationToken)
+ private async Task UpdateSeries(IEnumerable idList, IProgress 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);
- }
-
///
/// Dates the time to unix timestamp.
///
diff --git a/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs b/MediaBrowser.Providers/TV/ManualFanartSeasonProvider.cs
new file mode 100644
index 0000000000..71b96f0372
--- /dev/null
+++ b/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> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
+ {
+ var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
+
+ return images.Where(i => i.Type == imageType);
+ }
+
+ public Task> GetAllImages(BaseItem item, CancellationToken cancellationToken)
+ {
+ var list = new List();
+
+ var series = ((Season) item).Series;
+
+ if (series == null)
+ {
+ return Task.FromResult>(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>(list);
+ }
+
+ private void AddImages(List 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 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 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; }
+ }
+ }
+}