mirror of
https://github.com/jellyfin/jellyfin.git
synced 2024-11-15 09:59:06 -07:00
convert series providers to new system
This commit is contained in:
parent
b7a811992b
commit
48b9f657a4
@ -784,7 +784,8 @@ namespace MediaBrowser.Api.Images
|
||||
// Validate first
|
||||
using (var validationStream = new MemoryStream(bytes))
|
||||
{
|
||||
using (var image = Image.FromStream(validationStream))
|
||||
// This will throw an exception if it's not a valid image
|
||||
using (Image.FromStream(validationStream))
|
||||
{
|
||||
}
|
||||
}
|
||||
|
@ -224,6 +224,7 @@ namespace MediaBrowser.Model.Configuration
|
||||
EnableEpisodeChapterImageExtraction = false;
|
||||
EnableOtherVideoChapterImageExtraction = false;
|
||||
EnableAutomaticRestart = true;
|
||||
EnablePeoplePrefixSubFolders = true;
|
||||
|
||||
MinResumePct = 5;
|
||||
MaxResumePct = 90;
|
||||
|
@ -9,7 +9,7 @@ using System.Threading.Tasks;
|
||||
namespace MediaBrowser.Providers.BoxSets
|
||||
{
|
||||
/// <summary>
|
||||
/// Class SeriesProviderFromXml
|
||||
/// Class BoxSetXmlProvider.
|
||||
/// </summary>
|
||||
public class BoxSetXmlProvider : BaseXmlProvider, ILocalMetadataProvider<BoxSet>
|
||||
{
|
||||
|
@ -107,6 +107,8 @@
|
||||
<Compile Include="Music\ArtistMetadataService.cs" />
|
||||
<Compile Include="Music\LastfmArtistProvider.cs" />
|
||||
<Compile Include="Music\MusicBrainzArtistProvider.cs" />
|
||||
<Compile Include="Omdb\OmdbProvider.cs" />
|
||||
<Compile Include="Omdb\OmdbSeriesProvider.cs" />
|
||||
<Compile Include="People\MovieDbPersonImageProvider.cs" />
|
||||
<Compile Include="Movies\MovieUpdatesPrescanTask.cs" />
|
||||
<Compile Include="Movies\MovieXmlParser.cs" />
|
||||
@ -152,22 +154,20 @@
|
||||
<Compile Include="TV\EpisodeIndexNumberProvider.cs" />
|
||||
<Compile Include="TV\EpisodeProviderFromXml.cs" />
|
||||
<Compile Include="TV\EpisodeXmlParser.cs" />
|
||||
<Compile Include="TV\FanArtTVProvider.cs" />
|
||||
<Compile Include="TV\FanArtTvUpdatesPrescanTask.cs" />
|
||||
<Compile Include="TV\FanartSeasonProvider.cs" />
|
||||
<Compile Include="TV\ManualFanartSeriesProvider.cs" />
|
||||
<Compile Include="TV\ManualTvdbEpisodeImageProvider.cs" />
|
||||
<Compile Include="TV\FanartSeriesProvider.cs" />
|
||||
<Compile Include="TV\SeriesMetadataService.cs" />
|
||||
<Compile Include="TV\TvdbEpisodeImageProvider.cs" />
|
||||
<Compile Include="People\TvdbPersonImageProvider.cs" />
|
||||
<Compile Include="TV\TvdbSeasonImageProvider.cs" />
|
||||
<Compile Include="TV\ManualTvdbSeriesImageProvider.cs" />
|
||||
<Compile Include="TV\TvdbSeriesImageProvider.cs" />
|
||||
<Compile Include="TV\SeasonMetadataService.cs" />
|
||||
<Compile Include="TV\TvdbEpisodeProvider.cs" />
|
||||
<Compile Include="TV\TvdbSeriesImageProvider.cs" />
|
||||
<Compile Include="TV\TvdbSeriesProvider.cs" />
|
||||
<Compile Include="TV\SeasonXmlProvider.cs" />
|
||||
<Compile Include="TV\SeriesDynamicInfoProvider.cs" />
|
||||
<Compile Include="TV\SeriesPostScanTask.cs" />
|
||||
<Compile Include="TV\SeriesProviderFromXml.cs" />
|
||||
<Compile Include="TV\SeriesXmlProvider.cs" />
|
||||
<Compile Include="TV\SeriesXmlParser.cs" />
|
||||
<Compile Include="TV\TvdbPrescanTask.cs" />
|
||||
<Compile Include="UserRootFolderNameProvider.cs" />
|
||||
|
200
MediaBrowser.Providers/Omdb/OmdbProvider.cs
Normal file
200
MediaBrowser.Providers/Omdb/OmdbProvider.cs
Normal file
@ -0,0 +1,200 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.Omdb
|
||||
{
|
||||
public class OmdbProvider
|
||||
{
|
||||
private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public OmdbProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task Fetch(BaseItem item, CancellationToken cancellationToken)
|
||||
{
|
||||
var imdbId = item.GetProviderId(MetadataProviders.Imdb);
|
||||
|
||||
if (string.IsNullOrEmpty(imdbId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var imdbParam = imdbId.StartsWith("tt", StringComparison.OrdinalIgnoreCase) ? imdbId : "tt" + imdbId;
|
||||
|
||||
var url = string.Format("http://www.omdbapi.com/?i={0}&tomatoes=true", imdbParam);
|
||||
|
||||
using (var stream = await _httpClient.Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = _resourcePool,
|
||||
CancellationToken = cancellationToken
|
||||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
var result = _jsonSerializer.DeserializeFromStream<RootObject>(stream);
|
||||
|
||||
var hasCriticRating = item as IHasCriticRating;
|
||||
if (hasCriticRating != null)
|
||||
{
|
||||
// Seeing some bogus RT data on omdb for series, so filter it out here
|
||||
// RT doesn't even have tv series
|
||||
int tomatoMeter;
|
||||
|
||||
if (!string.IsNullOrEmpty(result.tomatoMeter)
|
||||
&& int.TryParse(result.tomatoMeter, NumberStyles.Integer, _usCulture, out tomatoMeter)
|
||||
&& tomatoMeter >= 0)
|
||||
{
|
||||
hasCriticRating.CriticRating = tomatoMeter;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result.tomatoConsensus)
|
||||
&& !string.Equals(result.tomatoConsensus, "n/a", StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(result.tomatoConsensus, "No consensus yet.", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hasCriticRating.CriticRatingSummary = WebUtility.HtmlDecode(result.tomatoConsensus);
|
||||
}
|
||||
}
|
||||
|
||||
int voteCount;
|
||||
|
||||
if (!string.IsNullOrEmpty(result.imdbVotes)
|
||||
&& int.TryParse(result.imdbVotes, NumberStyles.Number, _usCulture, out voteCount)
|
||||
&& voteCount >= 0)
|
||||
{
|
||||
item.VoteCount = voteCount;
|
||||
}
|
||||
|
||||
float imdbRating;
|
||||
|
||||
if (!string.IsNullOrEmpty(result.imdbRating)
|
||||
&& float.TryParse(result.imdbRating, NumberStyles.Any, _usCulture, out imdbRating)
|
||||
&& imdbRating >= 0)
|
||||
{
|
||||
item.CommunityRating = imdbRating;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Website)
|
||||
&& !string.Equals(result.Website, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.HomePageUrl = result.Website;
|
||||
}
|
||||
|
||||
ParseAdditionalMetadata(item, result);
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseAdditionalMetadata(BaseItem item, RootObject result)
|
||||
{
|
||||
// Grab series genres because imdb data is better than tvdb. Leave movies alone
|
||||
// But only do it if english is the preferred language because this data will not be localized
|
||||
if (!item.LockedFields.Contains(MetadataFields.Genres) &&
|
||||
ShouldFetchGenres(item) &&
|
||||
!string.IsNullOrWhiteSpace(result.Genre) &&
|
||||
!string.Equals(result.Genre, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
item.Genres.Clear();
|
||||
|
||||
foreach (var genre in result.Genre
|
||||
.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(i => i.Trim())
|
||||
.Where(i => !string.IsNullOrWhiteSpace(i)))
|
||||
{
|
||||
item.AddGenre(genre);
|
||||
}
|
||||
}
|
||||
|
||||
var hasMetascore = item as IHasMetascore;
|
||||
if (hasMetascore != null)
|
||||
{
|
||||
float metascore;
|
||||
|
||||
if (!string.IsNullOrEmpty(result.Metascore) && float.TryParse(result.Metascore, NumberStyles.Any, _usCulture, out metascore) && metascore >= 0)
|
||||
{
|
||||
hasMetascore.Metascore = metascore;
|
||||
}
|
||||
}
|
||||
|
||||
var hasAwards = item as IHasAwards;
|
||||
if (hasAwards != null && !string.IsNullOrEmpty(result.Awards) &&
|
||||
!string.Equals(result.Awards, "n/a", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
hasAwards.AwardSummary = WebUtility.HtmlDecode(result.Awards);
|
||||
}
|
||||
}
|
||||
|
||||
private bool ShouldFetchGenres(BaseItem item)
|
||||
{
|
||||
var lang = item.GetPreferredMetadataLanguage();
|
||||
|
||||
// The data isn't localized and so can only be used for english users
|
||||
if (!string.Equals(lang, "en", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only fetch if other providers didn't get anything
|
||||
if (item is Trailer)
|
||||
{
|
||||
return item.Genres.Count == 0;
|
||||
}
|
||||
|
||||
return item is Series || item is Movie;
|
||||
}
|
||||
|
||||
protected class RootObject
|
||||
{
|
||||
public string Title { get; set; }
|
||||
public string Year { get; set; }
|
||||
public string Rated { get; set; }
|
||||
public string Released { get; set; }
|
||||
public string Runtime { get; set; }
|
||||
public string Genre { get; set; }
|
||||
public string Director { get; set; }
|
||||
public string Writer { get; set; }
|
||||
public string Actors { get; set; }
|
||||
public string Plot { get; set; }
|
||||
public string Poster { get; set; }
|
||||
public string imdbRating { get; set; }
|
||||
public string imdbVotes { get; set; }
|
||||
public string imdbID { get; set; }
|
||||
public string Type { get; set; }
|
||||
public string tomatoMeter { get; set; }
|
||||
public string tomatoImage { get; set; }
|
||||
public string tomatoRating { get; set; }
|
||||
public string tomatoReviews { get; set; }
|
||||
public string tomatoFresh { get; set; }
|
||||
public string tomatoRotten { get; set; }
|
||||
public string tomatoConsensus { get; set; }
|
||||
public string tomatoUserMeter { get; set; }
|
||||
public string tomatoUserRating { get; set; }
|
||||
public string tomatoUserReviews { get; set; }
|
||||
public string DVD { get; set; }
|
||||
public string BoxOffice { get; set; }
|
||||
public string Production { get; set; }
|
||||
public string Website { get; set; }
|
||||
public string Response { get; set; }
|
||||
|
||||
public string Language { get; set; }
|
||||
public string Country { get; set; }
|
||||
public string Awards { get; set; }
|
||||
public string Metascore { get; set; }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
31
MediaBrowser.Providers/Omdb/OmdbSeriesProvider.cs
Normal file
31
MediaBrowser.Providers/Omdb/OmdbSeriesProvider.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Serialization;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.Omdb
|
||||
{
|
||||
public class OmdbSeriesProvider : ICustomMetadataProvider<Series>
|
||||
{
|
||||
private readonly IJsonSerializer _jsonSerializer;
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
public OmdbSeriesProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient)
|
||||
{
|
||||
_jsonSerializer = jsonSerializer;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Task FetchAsync(Series item, CancellationToken cancellationToken)
|
||||
{
|
||||
return new OmdbProvider(_jsonSerializer, _httpClient).Fetch(item, cancellationToken);
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return "OMDb"; }
|
||||
}
|
||||
}
|
||||
}
|
@ -20,14 +20,14 @@ using System.Xml;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
public class FanartSeasonImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor
|
||||
public class FanartSeasonProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor
|
||||
{
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public FanartSeasonImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
public FanartSeasonProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
{
|
||||
_config = config;
|
||||
_httpClient = httpClient;
|
||||
@ -78,9 +78,9 @@ namespace MediaBrowser.Providers.TV
|
||||
|
||||
if (!string.IsNullOrEmpty(id) && season.IndexNumber.HasValue)
|
||||
{
|
||||
await FanArtTvProvider.Current.EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false);
|
||||
await FanartSeriesProvider.Current.EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id);
|
||||
var xmlPath = FanartSeriesProvider.Current.GetFanartXmlPath(id);
|
||||
|
||||
try
|
||||
{
|
||||
@ -290,7 +290,7 @@ namespace MediaBrowser.Providers.TV
|
||||
if (!String.IsNullOrEmpty(tvdbId))
|
||||
{
|
||||
// Process images
|
||||
var imagesXmlPath = FanArtTvProvider.Current.GetFanartXmlPath(tvdbId);
|
||||
var imagesXmlPath = FanartSeriesProvider.Current.GetFanartXmlPath(tvdbId);
|
||||
|
||||
var fileInfo = new FileInfo(imagesXmlPath);
|
||||
|
||||
|
@ -1,331 +0,0 @@
|
||||
using MediaBrowser.Common.Configuration;
|
||||
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.Entities;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using MediaBrowser.Model.Net;
|
||||
using System.Net;
|
||||
using MediaBrowser.Providers.Music;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
class FanArtTvProvider : BaseMetadataProvider
|
||||
{
|
||||
protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/series/{0}/{1}/xml/all/1/1";
|
||||
|
||||
internal static FanArtTvProvider Current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the HTTP client.
|
||||
/// </summary>
|
||||
/// <value>The HTTP client.</value>
|
||||
protected IHttpClient HttpClient { get; private set; }
|
||||
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public FanArtTvProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
|
||||
: base(logManager, configurationManager)
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
throw new ArgumentNullException("httpClient");
|
||||
}
|
||||
HttpClient = httpClient;
|
||||
_providerManager = providerManager;
|
||||
_fileSystem = fileSystem;
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public override bool Supports(BaseItem item)
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public override MetadataProviderPriority Priority
|
||||
{
|
||||
get { return MetadataProviderPriority.Third; }
|
||||
}
|
||||
|
||||
public override ItemUpdateType ItemUpdateType
|
||||
{
|
||||
get
|
||||
{
|
||||
return ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Needses the refresh internal.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="providerInfo">The provider info.</param>
|
||||
/// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
|
||||
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
|
||||
{
|
||||
if (string.IsNullOrEmpty(item.GetProviderId(MetadataProviders.Tvdb)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return base.NeedsRefreshInternal(item, providerInfo);
|
||||
}
|
||||
|
||||
protected override bool NeedsRefreshBasedOnCompareDate(BaseItem item, BaseProviderInfo providerInfo)
|
||||
{
|
||||
var id = item.GetProviderId(MetadataProviders.Tvdb);
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
// Process images
|
||||
var xmlPath = GetFanartXmlPath(id);
|
||||
|
||||
var fileInfo = new FileInfo(xmlPath);
|
||||
|
||||
return !fileInfo.Exists || _fileSystem.GetLastWriteTimeUtc(fileInfo) > providerInfo.LastRefreshed;
|
||||
}
|
||||
|
||||
return base.NeedsRefreshBasedOnCompareDate(item, providerInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether [refresh on version change].
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if [refresh on version change]; otherwise, <c>false</c>.</value>
|
||||
protected override bool RefreshOnVersionChange
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider version.
|
||||
/// </summary>
|
||||
/// <value>The provider version.</value>
|
||||
protected override string ProviderVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the series data path.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <param name="seriesId">The series id.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId)
|
||||
{
|
||||
var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
|
||||
|
||||
return seriesDataPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the series data path.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths)
|
||||
{
|
||||
var dataPath = Path.Combine(appPaths.DataPath, "fanart-tv");
|
||||
|
||||
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");
|
||||
|
||||
public override async Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var seriesId = item.GetProviderId(MetadataProviders.Tvdb);
|
||||
|
||||
if (!string.IsNullOrEmpty(seriesId))
|
||||
{
|
||||
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(seriesId, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualFanartSeriesImageProvider.ProviderName).ConfigureAwait(false);
|
||||
|
||||
await FetchFromXml(item, images.ToList(), cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetches from XML.
|
||||
/// </summary>
|
||||
/// <param name="item">The item.</param>
|
||||
/// <param name="images">The images.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
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();
|
||||
|
||||
if (options.IsEnabled(ImageType.Primary) && !item.HasImage(ImageType.Primary))
|
||||
{
|
||||
await SaveImage(item, images, ImageType.Primary, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (options.IsEnabled(ImageType.Logo) && !item.HasImage(ImageType.Logo))
|
||||
{
|
||||
await SaveImage(item, images, ImageType.Logo, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (options.IsEnabled(ImageType.Art) && !item.HasImage(ImageType.Art))
|
||||
{
|
||||
await SaveImage(item, images, ImageType.Art, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (options.IsEnabled(ImageType.Thumb) && !item.HasImage(ImageType.Thumb))
|
||||
{
|
||||
await SaveImage(item, images, ImageType.Thumb, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (options.IsEnabled(ImageType.Banner) && !item.HasImage(ImageType.Banner))
|
||||
{
|
||||
await SaveImage(item, images, ImageType.Banner, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!item.LockedFields.Contains(MetadataFields.Backdrops))
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var backdropLimit = options.GetLimit(ImageType.Backdrop);
|
||||
if (options.IsEnabled(ImageType.Backdrop) &&
|
||||
item.BackdropImagePaths.Count < backdropLimit)
|
||||
{
|
||||
foreach (var image in images.Where(i => i.Type == ImageType.Backdrop))
|
||||
{
|
||||
await _providerManager.SaveImage(item, image.Url, FanartArtistProvider.FanArtResourcePool, 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, FanartArtistProvider.FanArtResourcePool, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Task _cachedTask = Task.FromResult(true);
|
||||
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 _cachedTask;
|
||||
}
|
||||
}
|
||||
|
||||
return DownloadSeriesXml(tvdbId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the series XML.
|
||||
/// </summary>
|
||||
/// <param name="tvdbId">The TVDB id.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
internal async Task DownloadSeriesXml(string tvdbId, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tvdbId);
|
||||
|
||||
var xmlPath = GetFanartXmlPath(tvdbId);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
|
||||
|
||||
using (var response = await HttpClient.Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = FanartArtistProvider.FanArtResourcePool,
|
||||
CancellationToken = cancellationToken
|
||||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
{
|
||||
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -60,7 +60,7 @@ namespace MediaBrowser.Providers.TV
|
||||
return;
|
||||
}
|
||||
|
||||
var path = FanArtTvProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
|
||||
var path = FanartSeriesProvider.GetSeriesDataPath(_config.CommonApplicationPaths);
|
||||
|
||||
Directory.CreateDirectory(path);
|
||||
|
||||
@ -149,8 +149,8 @@ namespace MediaBrowser.Providers.TV
|
||||
foreach (var id in list)
|
||||
{
|
||||
_logger.Info("Updating series " + id);
|
||||
|
||||
await FanArtTvProvider.Current.DownloadSeriesXml(id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
await FanartSeriesProvider.Current.DownloadSeriesXml(id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
numComplete++;
|
||||
double percent = numComplete;
|
||||
|
@ -1,4 +1,6 @@
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Common.Configuration;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Common.Net;
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
@ -6,6 +8,7 @@ using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Dto;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using MediaBrowser.Providers.Music;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
@ -15,20 +18,27 @@ using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml;
|
||||
using MediaBrowser.Providers.Music;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
public class ManualFanartSeriesImageProvider : IRemoteImageProvider, IHasOrder
|
||||
public class FanartSeriesProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor
|
||||
{
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public ManualFanartSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
|
||||
protected string FanArtBaseUrl = "http://api.fanart.tv/webservice/series/{0}/{1}/xml/all/1/1";
|
||||
|
||||
internal static FanartSeriesProvider Current { get; private set; }
|
||||
|
||||
public FanartSeriesProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
{
|
||||
_config = config;
|
||||
_httpClient = httpClient;
|
||||
_fileSystem = fileSystem;
|
||||
|
||||
Current = this;
|
||||
}
|
||||
|
||||
public string Name
|
||||
@ -66,7 +76,7 @@ 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 list = new List<RemoteImageInfo>();
|
||||
|
||||
@ -76,7 +86,9 @@ namespace MediaBrowser.Providers.TV
|
||||
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
var xmlPath = FanArtTvProvider.Current.GetFanartXmlPath(id);
|
||||
await EnsureSeriesXml(id, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
var xmlPath = GetFanartXmlPath(id);
|
||||
|
||||
try
|
||||
{
|
||||
@ -93,7 +105,7 @@ namespace MediaBrowser.Providers.TV
|
||||
var isLanguageEn = string.Equals(language, "en", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
// Sort first by width to prioritize HD versions
|
||||
list = list.OrderByDescending(i => i.Width ?? 0)
|
||||
return list.OrderByDescending(i => i.Width ?? 0)
|
||||
.ThenByDescending(i =>
|
||||
{
|
||||
if (string.Equals(language, i.Language, StringComparison.OrdinalIgnoreCase))
|
||||
@ -114,10 +126,7 @@ namespace MediaBrowser.Providers.TV
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(i => i.CommunityRating ?? 0)
|
||||
.ThenByDescending(i => i.VoteCount ?? 0)
|
||||
.ToList();
|
||||
|
||||
return Task.FromResult<IEnumerable<RemoteImageInfo>>(list);
|
||||
.ThenByDescending(i => i.VoteCount ?? 0);
|
||||
}
|
||||
|
||||
private void AddImages(List<RemoteImageInfo> list, string xmlPath, CancellationToken cancellationToken)
|
||||
@ -333,5 +342,102 @@ namespace MediaBrowser.Providers.TV
|
||||
ResourcePool = FanartArtistProvider.FanArtResourcePool
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the series data path.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <param name="seriesId">The series id.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths, string seriesId)
|
||||
{
|
||||
var seriesDataPath = Path.Combine(GetSeriesDataPath(appPaths), seriesId);
|
||||
|
||||
return seriesDataPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the series data path.
|
||||
/// </summary>
|
||||
/// <param name="appPaths">The app paths.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
internal static string GetSeriesDataPath(IApplicationPaths appPaths)
|
||||
{
|
||||
var dataPath = Path.Combine(appPaths.DataPath, "fanart-tv");
|
||||
|
||||
return dataPath;
|
||||
}
|
||||
|
||||
public string GetFanartXmlPath(string tvdbId)
|
||||
{
|
||||
var dataPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId);
|
||||
return Path.Combine(dataPath, "fanart.xml");
|
||||
}
|
||||
|
||||
private readonly Task _cachedTask = Task.FromResult(true);
|
||||
internal Task EnsureSeriesXml(string tvdbId, CancellationToken cancellationToken)
|
||||
{
|
||||
var xmlPath = GetSeriesDataPath(_config.ApplicationPaths, tvdbId);
|
||||
|
||||
var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath);
|
||||
|
||||
if (fileInfo.Exists)
|
||||
{
|
||||
if (_config.Configuration.EnableFanArtUpdates || (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 7)
|
||||
{
|
||||
return _cachedTask;
|
||||
}
|
||||
}
|
||||
|
||||
return DownloadSeriesXml(tvdbId, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Downloads the series XML.
|
||||
/// </summary>
|
||||
/// <param name="tvdbId">The TVDB id.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>Task.</returns>
|
||||
internal async Task DownloadSeriesXml(string tvdbId, CancellationToken cancellationToken)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var url = string.Format(FanArtBaseUrl, FanartArtistProvider.ApiKey, tvdbId);
|
||||
|
||||
var xmlPath = GetFanartXmlPath(tvdbId);
|
||||
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(xmlPath));
|
||||
|
||||
using (var response = await _httpClient.Get(new HttpRequestOptions
|
||||
{
|
||||
Url = url,
|
||||
ResourcePool = FanartArtistProvider.FanArtResourcePool,
|
||||
CancellationToken = cancellationToken
|
||||
|
||||
}).ConfigureAwait(false))
|
||||
{
|
||||
using (var xmlFileStream = _fileSystem.GetFileStream(xmlPath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
|
||||
{
|
||||
await response.CopyToAsync(xmlFileStream).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasChanged(IHasMetadata item, DateTime date)
|
||||
{
|
||||
var tvdbId = item.GetProviderId(MetadataProviders.Tvdb);
|
||||
|
||||
if (!String.IsNullOrEmpty(tvdbId))
|
||||
{
|
||||
// Process images
|
||||
var imagesXmlPath = GetFanartXmlPath(tvdbId);
|
||||
|
||||
var fileInfo = new FileInfo(imagesXmlPath);
|
||||
|
||||
return fileInfo.Exists && _fileSystem.GetLastWriteTimeUtc(fileInfo) > date;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
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.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 ManualTvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
|
||||
public ManualTvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
|
||||
{
|
||||
_config = config;
|
||||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return ProviderName; }
|
||||
}
|
||||
|
||||
public static string ProviderName
|
||||
{
|
||||
get { return "TheTVDB"; }
|
||||
}
|
||||
|
||||
public bool Supports(IHasImages item)
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
|
||||
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
|
||||
{
|
||||
return new List<ImageType>
|
||||
{
|
||||
ImageType.Primary,
|
||||
ImageType.Banner,
|
||||
ImageType.Backdrop
|
||||
};
|
||||
}
|
||||
|
||||
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 series = (Series)item;
|
||||
var seriesId = series.GetProviderId(MetadataProviders.Tvdb);
|
||||
|
||||
if (!string.IsNullOrEmpty(seriesId))
|
||||
{
|
||||
// Process images
|
||||
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId);
|
||||
|
||||
var path = Path.Combine(seriesDataPath, "banners.xml");
|
||||
|
||||
try
|
||||
{
|
||||
var result = GetImages(path, item.GetPreferredMetadataLanguage(), cancellationToken);
|
||||
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// No tvdb data yet. Don't blow up
|
||||
}
|
||||
}
|
||||
|
||||
return Task.FromResult<IEnumerable<RemoteImageInfo>>(new RemoteImageInfo[] { });
|
||||
}
|
||||
|
||||
private IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken)
|
||||
{
|
||||
var settings = new XmlReaderSettings
|
||||
{
|
||||
CheckCharacters = false,
|
||||
IgnoreProcessingInstructions = true,
|
||||
IgnoreComments = true,
|
||||
ValidationType = ValidationType.None
|
||||
};
|
||||
|
||||
var list = new List<RemoteImageInfo>();
|
||||
|
||||
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
|
||||
{
|
||||
// Use XmlReader for best performance
|
||||
using (var reader = XmlReader.Create(streamReader, settings))
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
// Loop through each element
|
||||
while (reader.Read())
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Banner":
|
||||
{
|
||||
using (var subtree = reader.ReadSubtree())
|
||||
{
|
||||
AddImage(subtree, list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return list.OrderByDescending(i =>
|
||||
{
|
||||
if (string.Equals(preferredLanguage, 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();
|
||||
}
|
||||
|
||||
private void AddImage(XmlReader reader, List<RemoteImageInfo> images)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
string bannerType = null;
|
||||
string url = null;
|
||||
int? bannerSeason = null;
|
||||
int? width = null;
|
||||
int? height = null;
|
||||
string language = null;
|
||||
double? rating = null;
|
||||
int? voteCount = null;
|
||||
string thumbnailUrl = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Rating":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
double rval;
|
||||
|
||||
if (double.TryParse(val, NumberStyles.Any, _usCulture, out rval))
|
||||
{
|
||||
rating = rval;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "RatingCount":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
int rval;
|
||||
|
||||
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
voteCount = rval;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Language":
|
||||
{
|
||||
language = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "ThumbnailPath":
|
||||
{
|
||||
thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerType":
|
||||
{
|
||||
bannerType = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerPath":
|
||||
{
|
||||
url = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerType2":
|
||||
{
|
||||
var bannerType2 = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
// Sometimes the resolution is stuffed in here
|
||||
var resolutionParts = bannerType2.Split('x');
|
||||
|
||||
if (resolutionParts.Length == 2)
|
||||
{
|
||||
int rval;
|
||||
|
||||
if (int.TryParse(resolutionParts[0], NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
width = rval;
|
||||
}
|
||||
|
||||
if (int.TryParse(resolutionParts[1], NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
height = rval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Season":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
bannerSeason = int.Parse(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue)
|
||||
{
|
||||
var imageInfo = new RemoteImageInfo
|
||||
{
|
||||
RatingType = RatingType.Score,
|
||||
CommunityRating = rating,
|
||||
VoteCount = voteCount,
|
||||
Url = TVUtils.BannerUrl + url,
|
||||
ProviderName = Name,
|
||||
Language = language,
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(thumbnailUrl))
|
||||
{
|
||||
imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl;
|
||||
}
|
||||
|
||||
if (string.Equals(bannerType, "poster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Primary;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
else if (string.Equals(bannerType, "series", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Banner;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Backdrop;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int Order
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return _httpClient.GetResponse(new HttpRequestOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
Url = url,
|
||||
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
using MediaBrowser.Controller.Configuration;
|
||||
using MediaBrowser.Controller.Entities;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
public class SeriesDynamicInfoProvider : BaseMetadataProvider, IDynamicInfoProvider
|
||||
{
|
||||
public SeriesDynamicInfoProvider(ILogManager logManager, IServerConfigurationManager configurationManager)
|
||||
: base(logManager, configurationManager)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool Supports(BaseItem item)
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
|
||||
public override Task<bool> FetchAsync(BaseItem item, bool force, BaseProviderInfo providerInfo, CancellationToken cancellationToken)
|
||||
{
|
||||
var series = (Series)item;
|
||||
|
||||
var episodes = series.RecursiveChildren
|
||||
.OfType<Episode>()
|
||||
.ToList();
|
||||
|
||||
series.DateLastEpisodeAdded = episodes.Select(i => i.DateCreated)
|
||||
.OrderByDescending(i => i)
|
||||
.FirstOrDefault();
|
||||
|
||||
// Don't save to the db
|
||||
return FalseTaskResult;
|
||||
}
|
||||
|
||||
public override MetadataProviderPriority Priority
|
||||
{
|
||||
get { return MetadataProviderPriority.Last; }
|
||||
}
|
||||
}
|
||||
}
|
66
MediaBrowser.Providers/TV/SeriesMetadataService.cs
Normal file
66
MediaBrowser.Providers/TV/SeriesMetadataService.cs
Normal file
@ -0,0 +1,66 @@
|
||||
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.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
public class SeriesMetadataService : MetadataService<Series, ItemId>
|
||||
{
|
||||
private readonly ILibraryManager _libraryManager;
|
||||
|
||||
public SeriesMetadataService(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(Series source, Series target, List<MetadataFields> lockedFields, bool replaceData, bool mergeMetadataSettings)
|
||||
{
|
||||
ProviderUtils.MergeBaseItemData(source, target, lockedFields, replaceData, mergeMetadataSettings);
|
||||
}
|
||||
|
||||
protected override Task SaveItem(Series item, ItemUpdateType reason, CancellationToken cancellationToken)
|
||||
{
|
||||
return _libraryManager.UpdateItem(item, reason, cancellationToken);
|
||||
}
|
||||
|
||||
protected override ItemUpdateType AfterMetadataRefresh(Series item)
|
||||
{
|
||||
var updateType = base.AfterMetadataRefresh(item);
|
||||
|
||||
var episodes = item.RecursiveChildren
|
||||
.OfType<Episode>()
|
||||
.ToList();
|
||||
|
||||
var dateLastEpisodeAdded = item.DateLastEpisodeAdded;
|
||||
|
||||
item.DateLastEpisodeAdded = episodes.Select(i => i.DateCreated)
|
||||
.OrderByDescending(i => i)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (dateLastEpisodeAdded != item.DateLastEpisodeAdded)
|
||||
{
|
||||
updateType = updateType | ItemUpdateType.MetadataImport;
|
||||
}
|
||||
|
||||
return updateType;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,95 +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 SeriesProviderFromXml : BaseMetadataProvider
|
||||
{
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
public SeriesProviderFromXml(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 Series && item.LocationType == LocationType.FileSystem;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public override MetadataProviderPriority Priority
|
||||
{
|
||||
get { return MetadataProviderPriority.First; }
|
||||
}
|
||||
|
||||
private const string XmlFileName = "series.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 SeriesXmlParser(Logger).Fetch((Series)item, path, cancellationToken);
|
||||
}
|
||||
finally
|
||||
{
|
||||
XmlParsingResourcePool.Release();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
62
MediaBrowser.Providers/TV/SeriesXmlProvider.cs
Normal file
62
MediaBrowser.Providers/TV/SeriesXmlProvider.cs
Normal file
@ -0,0 +1,62 @@
|
||||
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 SeriesXmlProvider : BaseXmlProvider, ILocalMetadataProvider<Series>
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public SeriesXmlProvider(IFileSystem fileSystem, ILogger logger)
|
||||
: base(fileSystem)
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public async Task<MetadataResult<Series>> GetMetadata(string path, CancellationToken cancellationToken)
|
||||
{
|
||||
path = GetXmlFile(path).FullName;
|
||||
|
||||
var result = new MetadataResult<Series>();
|
||||
|
||||
await XmlParsingResourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
|
||||
|
||||
try
|
||||
{
|
||||
var person = new Series();
|
||||
|
||||
new SeriesXmlParser(_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, "series.xml"));
|
||||
}
|
||||
}
|
||||
}
|
@ -17,13 +17,13 @@ using System.Xml;
|
||||
|
||||
namespace MediaBrowser.Providers.TV
|
||||
{
|
||||
public class ManualTvdbEpisodeImageProvider : IRemoteImageProvider
|
||||
public class TvdbEpisodeImageProvider : IRemoteImageProvider
|
||||
{
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly IHttpClient _httpClient;
|
||||
|
||||
public ManualTvdbEpisodeImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
|
||||
public TvdbEpisodeImageProvider(IServerConfigurationManager config, IHttpClient httpClient)
|
||||
{
|
||||
_config = config;
|
||||
_httpClient = httpClient;
|
@ -5,211 +5,353 @@ 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 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 TvdbSeriesImageProvider : BaseMetadataProvider
|
||||
public class TvdbSeriesImageProvider : IRemoteImageProvider, IHasOrder, IHasChangeMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the HTTP client.
|
||||
/// </summary>
|
||||
/// <value>The HTTP client.</value>
|
||||
protected IHttpClient HttpClient { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _provider manager
|
||||
/// </summary>
|
||||
private readonly IProviderManager _providerManager;
|
||||
private readonly IServerConfigurationManager _config;
|
||||
private readonly IHttpClient _httpClient;
|
||||
private readonly CultureInfo _usCulture = new CultureInfo("en-US");
|
||||
private readonly IFileSystem _fileSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TvdbSeriesImageProvider"/> class.
|
||||
/// </summary>
|
||||
/// <param name="httpClient">The HTTP client.</param>
|
||||
/// <param name="logManager">The log manager.</param>
|
||||
/// <param name="configurationManager">The configuration manager.</param>
|
||||
/// <param name="providerManager">The provider manager.</param>
|
||||
/// <exception cref="System.ArgumentNullException">httpClient</exception>
|
||||
public TvdbSeriesImageProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
|
||||
: base(logManager, configurationManager)
|
||||
public TvdbSeriesImageProvider(IServerConfigurationManager config, IHttpClient httpClient, IFileSystem fileSystem)
|
||||
{
|
||||
if (httpClient == null)
|
||||
{
|
||||
throw new ArgumentNullException("httpClient");
|
||||
}
|
||||
HttpClient = httpClient;
|
||||
_providerManager = providerManager;
|
||||
_config = config;
|
||||
_httpClient = httpClient;
|
||||
_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)
|
||||
public string Name
|
||||
{
|
||||
get { return ProviderName; }
|
||||
}
|
||||
|
||||
public static string ProviderName
|
||||
{
|
||||
get { return "TheTVDB"; }
|
||||
}
|
||||
|
||||
public bool Supports(IHasImages item)
|
||||
{
|
||||
return item is Series;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the priority.
|
||||
/// </summary>
|
||||
/// <value>The priority.</value>
|
||||
public override MetadataProviderPriority Priority
|
||||
public IEnumerable<ImageType> GetSupportedImages(IHasImages item)
|
||||
{
|
||||
// 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 new List<ImageType>
|
||||
{
|
||||
return true;
|
||||
}
|
||||
ImageType.Primary,
|
||||
ImageType.Banner,
|
||||
ImageType.Backdrop
|
||||
};
|
||||
}
|
||||
|
||||
public override ItemUpdateType ItemUpdateType
|
||||
public async Task<IEnumerable<RemoteImageInfo>> GetImages(IHasImages item, ImageType imageType, CancellationToken cancellationToken)
|
||||
{
|
||||
get
|
||||
{
|
||||
return ItemUpdateType.ImageUpdate;
|
||||
}
|
||||
var images = await GetAllImages(item, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
return images.Where(i => i.Type == imageType);
|
||||
}
|
||||
|
||||
/// <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 async Task<IEnumerable<RemoteImageInfo>> GetAllImages(IHasImages item, CancellationToken cancellationToken)
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the provider version.
|
||||
/// </summary>
|
||||
/// <value>The provider version.</value>
|
||||
protected override string ProviderVersion
|
||||
{
|
||||
get
|
||||
{
|
||||
return "1";
|
||||
}
|
||||
}
|
||||
|
||||
protected override DateTime CompareDate(BaseItem item)
|
||||
{
|
||||
var seriesId = item.GetProviderId(MetadataProviders.Tvdb);
|
||||
var series = (Series)item;
|
||||
var seriesId = series.GetProviderId(MetadataProviders.Tvdb);
|
||||
|
||||
if (!string.IsNullOrEmpty(seriesId))
|
||||
{
|
||||
var language = item.GetPreferredMetadataLanguage();
|
||||
|
||||
await TvdbSeriesProvider.Current.EnsureSeriesInfo(seriesId, language, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
// Process images
|
||||
var imagesXmlPath = Path.Combine(TvdbSeriesProvider.GetSeriesDataPath(ConfigurationManager.ApplicationPaths, seriesId), "banners.xml");
|
||||
var seriesDataPath = TvdbSeriesProvider.GetSeriesDataPath(_config.ApplicationPaths, seriesId);
|
||||
|
||||
var imagesFileInfo = new FileInfo(imagesXmlPath);
|
||||
var path = Path.Combine(seriesDataPath, "banners.xml");
|
||||
|
||||
if (imagesFileInfo.Exists)
|
||||
try
|
||||
{
|
||||
return _fileSystem.GetLastWriteTimeUtc(imagesFileInfo);
|
||||
return GetImages(path, language, cancellationToken);
|
||||
}
|
||||
catch (FileNotFoundException)
|
||||
{
|
||||
// No tvdb data yet. Don't blow up
|
||||
}
|
||||
}
|
||||
|
||||
return base.CompareDate(item);
|
||||
return new RemoteImageInfo[] { };
|
||||
}
|
||||
|
||||
protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
|
||||
private IEnumerable<RemoteImageInfo> GetImages(string xmlPath, string preferredLanguage, CancellationToken cancellationToken)
|
||||
{
|
||||
var options = ConfigurationManager.Configuration.GetMetadataOptions("Series") ?? new MetadataOptions();
|
||||
|
||||
if (item.HasImage(ImageType.Primary) && item.HasImage(ImageType.Banner) && item.BackdropImagePaths.Count >= options.GetLimit(ImageType.Backdrop))
|
||||
var settings = new XmlReaderSettings
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return base.NeedsRefreshInternal(item, providerInfo);
|
||||
}
|
||||
CheckCharacters = false,
|
||||
IgnoreProcessingInstructions = true,
|
||||
IgnoreComments = true,
|
||||
ValidationType = ValidationType.None
|
||||
};
|
||||
|
||||
/// <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 list = new List<RemoteImageInfo>();
|
||||
|
||||
var images = await _providerManager.GetAvailableRemoteImages(item, cancellationToken, ManualTvdbSeriesImageProvider.ProviderName).ConfigureAwait(false);
|
||||
|
||||
const int backdropLimit = 1;
|
||||
|
||||
await DownloadImages(item, images.ToList(), backdropLimit, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
SetLastRefreshed(item, DateTime.UtcNow, providerInfo);
|
||||
return true;
|
||||
}
|
||||
|
||||
private async Task DownloadImages(BaseItem item, List<RemoteImageInfo> images, int backdropLimit, CancellationToken cancellationToken)
|
||||
{
|
||||
var options = ConfigurationManager.Configuration.GetMetadataOptions("Series") ?? new MetadataOptions();
|
||||
|
||||
if (!item.LockedFields.Contains(MetadataFields.Images))
|
||||
using (var streamReader = new StreamReader(xmlPath, Encoding.UTF8))
|
||||
{
|
||||
if (!item.HasImage(ImageType.Primary))
|
||||
// Use XmlReader for best performance
|
||||
using (var reader = XmlReader.Create(streamReader, settings))
|
||||
{
|
||||
var image = images.FirstOrDefault(i => i.Type == ImageType.Primary);
|
||||
reader.MoveToContent();
|
||||
|
||||
if (image != null)
|
||||
// Loop through each element
|
||||
while (reader.Read())
|
||||
{
|
||||
await _providerManager.SaveImage(item, image.Url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Primary, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
if (options.IsEnabled(ImageType.Banner) && !item.HasImage(ImageType.Banner))
|
||||
{
|
||||
var image = images.FirstOrDefault(i => i.Type == ImageType.Banner);
|
||||
|
||||
if (image != null)
|
||||
{
|
||||
await _providerManager.SaveImage(item, image.Url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Banner, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Banner":
|
||||
{
|
||||
using (var subtree = reader.ReadSubtree())
|
||||
{
|
||||
AddImage(subtree, list);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.IsEnabled(ImageType.Backdrop) && item.BackdropImagePaths.Count < backdropLimit && !item.LockedFields.Contains(MetadataFields.Backdrops))
|
||||
var isLanguageEn = string.Equals(preferredLanguage, "en", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
return list.OrderByDescending(i =>
|
||||
{
|
||||
foreach (var backdrop in images.Where(i => i.Type == ImageType.Backdrop &&
|
||||
(!i.Width.HasValue ||
|
||||
i.Width.Value >= options.GetMinWidth(ImageType.Backdrop))))
|
||||
if (string.Equals(preferredLanguage, i.Language, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var url = backdrop.Url;
|
||||
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();
|
||||
}
|
||||
|
||||
await _providerManager.SaveImage(item, url, TvdbSeriesProvider.Current.TvDbResourcePool, ImageType.Backdrop, null, cancellationToken).ConfigureAwait(false);
|
||||
private void AddImage(XmlReader reader, List<RemoteImageInfo> images)
|
||||
{
|
||||
reader.MoveToContent();
|
||||
|
||||
if (item.BackdropImagePaths.Count >= backdropLimit) break;
|
||||
string bannerType = null;
|
||||
string url = null;
|
||||
int? bannerSeason = null;
|
||||
int? width = null;
|
||||
int? height = null;
|
||||
string language = null;
|
||||
double? rating = null;
|
||||
int? voteCount = null;
|
||||
string thumbnailUrl = null;
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
switch (reader.Name)
|
||||
{
|
||||
case "Rating":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
double rval;
|
||||
|
||||
if (double.TryParse(val, NumberStyles.Any, _usCulture, out rval))
|
||||
{
|
||||
rating = rval;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "RatingCount":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
int rval;
|
||||
|
||||
if (int.TryParse(val, NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
voteCount = rval;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Language":
|
||||
{
|
||||
language = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "ThumbnailPath":
|
||||
{
|
||||
thumbnailUrl = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerType":
|
||||
{
|
||||
bannerType = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerPath":
|
||||
{
|
||||
url = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
break;
|
||||
}
|
||||
|
||||
case "BannerType2":
|
||||
{
|
||||
var bannerType2 = reader.ReadElementContentAsString() ?? string.Empty;
|
||||
|
||||
// Sometimes the resolution is stuffed in here
|
||||
var resolutionParts = bannerType2.Split('x');
|
||||
|
||||
if (resolutionParts.Length == 2)
|
||||
{
|
||||
int rval;
|
||||
|
||||
if (int.TryParse(resolutionParts[0], NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
width = rval;
|
||||
}
|
||||
|
||||
if (int.TryParse(resolutionParts[1], NumberStyles.Integer, _usCulture, out rval))
|
||||
{
|
||||
height = rval;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "Season":
|
||||
{
|
||||
var val = reader.ReadElementContentAsString();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(val))
|
||||
{
|
||||
bannerSeason = int.Parse(val);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
default:
|
||||
reader.Skip();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(url) && !bannerSeason.HasValue)
|
||||
{
|
||||
var imageInfo = new RemoteImageInfo
|
||||
{
|
||||
RatingType = RatingType.Score,
|
||||
CommunityRating = rating,
|
||||
VoteCount = voteCount,
|
||||
Url = TVUtils.BannerUrl + url,
|
||||
ProviderName = Name,
|
||||
Language = language,
|
||||
Width = width,
|
||||
Height = height
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(thumbnailUrl))
|
||||
{
|
||||
imageInfo.ThumbnailUrl = TVUtils.BannerUrl + thumbnailUrl;
|
||||
}
|
||||
|
||||
if (string.Equals(bannerType, "poster", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Primary;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
else if (string.Equals(bannerType, "series", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Banner;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
else if (string.Equals(bannerType, "fanart", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
imageInfo.Type = ImageType.Backdrop;
|
||||
images.Add(imageInfo);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int Order
|
||||
{
|
||||
get { return 0; }
|
||||
}
|
||||
|
||||
public Task<HttpResponseInfo> GetImageResponse(string url, CancellationToken cancellationToken)
|
||||
{
|
||||
return _httpClient.GetResponse(new HttpRequestOptions
|
||||
{
|
||||
CancellationToken = cancellationToken,
|
||||
Url = url,
|
||||
ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool
|
||||
});
|
||||
}
|
||||
|
||||
public bool HasChanged(IHasMetadata item, DateTime date)
|
||||
{
|
||||
var tvdbId = item.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -208,7 +208,6 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
/// <param name="prescanTasks">The prescan tasks.</param>
|
||||
/// <param name="postscanTasks">The postscan tasks.</param>
|
||||
/// <param name="peoplePrescanTasks">The people prescan tasks.</param>
|
||||
/// <param name="savers">The savers.</param>
|
||||
public void AddParts(IEnumerable<IResolverIgnoreRule> rules,
|
||||
IEnumerable<IVirtualFolderCreator> pluginFolders,
|
||||
IEnumerable<IItemResolver> resolvers,
|
||||
@ -277,7 +276,7 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
/// <param name="configuration">The configuration.</param>
|
||||
private void RecordConfigurationValues(ServerConfiguration configuration)
|
||||
{
|
||||
_seasonZeroDisplayName = ConfigurationManager.Configuration.SeasonZeroDisplayName;
|
||||
_seasonZeroDisplayName = configuration.SeasonZeroDisplayName;
|
||||
_itemsByNamePath = ConfigurationManager.ApplicationPaths.ItemsByNamePath;
|
||||
}
|
||||
|
||||
@ -309,8 +308,10 @@ namespace MediaBrowser.Server.Implementations.Library
|
||||
await UpdateSeasonZeroNames(newSeasonZeroName, CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Any number of configuration settings could change the way the library is refreshed, so do that now
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
|
||||
if (seasonZeroNameChanged || ibnPathChanged)
|
||||
{
|
||||
_taskManager.CancelIfRunningAndQueue<RefreshMediaLibraryTask>();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user