using MediaBrowser.Common.Extensions; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities; using MediaBrowser.Model.Logging; using System; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Controller.Providers { /// /// Class BaseMetadataProvider /// public abstract class BaseMetadataProvider { /// /// Gets the logger. /// /// The logger. protected ILogger Logger { get; set; } protected ILogManager LogManager { get; set; } /// /// Gets the configuration manager. /// /// The configuration manager. protected IServerConfigurationManager ConfigurationManager { get; private set; } /// /// The _id /// protected readonly Guid Id; protected static readonly SemaphoreSlim XmlParsingResourcePool = new SemaphoreSlim(5, 5); /// /// Supportses the specified item. /// /// The item. /// true if XXXX, false otherwise public abstract bool Supports(BaseItem item); /// /// Gets a value indicating whether [requires internet]. /// /// true if [requires internet]; otherwise, false. public virtual bool RequiresInternet { get { return false; } } /// /// Gets the provider version. /// /// The provider version. protected virtual string ProviderVersion { get { return null; } } /// /// Gets a value indicating whether [refresh on version change]. /// /// true if [refresh on version change]; otherwise, false. protected virtual bool RefreshOnVersionChange { get { return false; } } /// /// Determines if this provider is relatively slow and, therefore, should be skipped /// in certain instances. Default is whether or not it requires internet. Can be overridden /// for explicit designation. /// /// true if this instance is slow; otherwise, false. public virtual bool IsSlow { get { return RequiresInternet; } } /// /// Initializes a new instance of the class. /// protected BaseMetadataProvider(ILogManager logManager, IServerConfigurationManager configurationManager) { Logger = logManager.GetLogger(GetType().Name); LogManager = logManager; ConfigurationManager = configurationManager; Id = GetType().FullName.GetMD5(); Initialize(); } /// /// Initializes this instance. /// protected virtual void Initialize() { } /// /// Sets the persisted last refresh date on the item for this provider. /// /// The item. /// The value. /// The provider version. /// The status. /// item public virtual void SetLastRefreshed(BaseItem item, DateTime value, string providerVersion, ProviderRefreshStatus status = ProviderRefreshStatus.Success) { if (item == null) { throw new ArgumentNullException("item"); } BaseProviderInfo data; if (!item.ProviderData.TryGetValue(Id, out data)) { data = new BaseProviderInfo(); } data.LastRefreshed = value; data.LastRefreshStatus = status; data.ProviderVersion = providerVersion; // Save the file system stamp for future comparisons if (RefreshOnFileSystemStampChange) { data.FileSystemStamp = GetCurrentFileSystemStamp(item); } item.ProviderData[Id] = data; } /// /// Sets the last refreshed. /// /// The item. /// The value. /// The status. public void SetLastRefreshed(BaseItem item, DateTime value, ProviderRefreshStatus status = ProviderRefreshStatus.Success) { SetLastRefreshed(item, value, ProviderVersion, status); } /// /// Returns whether or not this provider should be re-fetched. Default functionality can /// compare a provided date with a last refresh time. This can be overridden for more complex /// determinations. /// /// The item. /// true if XXXX, false otherwise /// public bool NeedsRefresh(BaseItem item) { if (item == null) { throw new ArgumentNullException(); } BaseProviderInfo data; if (!item.ProviderData.TryGetValue(Id, out data)) { data = new BaseProviderInfo(); } return NeedsRefreshInternal(item, data); } /// /// Needses the refresh internal. /// /// The item. /// The provider info. /// true if XXXX, false otherwise /// protected virtual bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo) { if (item == null) { throw new ArgumentNullException("item"); } if (providerInfo == null) { throw new ArgumentNullException("providerInfo"); } if (CompareDate(item) > providerInfo.LastRefreshed) { return true; } if (RefreshOnFileSystemStampChange && HasFileSystemStampChanged(item, providerInfo)) { return true; } if (RefreshOnVersionChange && !string.Equals(ProviderVersion, providerInfo.ProviderVersion)) { return true; } return false; } /// /// Determines if the item's file system stamp has changed from the last time the provider refreshed /// /// The item. /// The provider info. /// true if [has file system stamp changed] [the specified item]; otherwise, false. protected bool HasFileSystemStampChanged(BaseItem item, BaseProviderInfo providerInfo) { return !string.Equals(GetCurrentFileSystemStamp(item), providerInfo.FileSystemStamp); } /// /// Override this to return the date that should be compared to the last refresh date /// to determine if this provider should be re-fetched. /// /// The item. /// DateTime. protected virtual DateTime CompareDate(BaseItem item) { return DateTime.MinValue.AddMinutes(1); // want this to be greater than mindate so new items will refresh } /// /// Fetches metadata and returns true or false indicating if any work that requires persistence was done /// /// The item. /// if set to true [force]. /// The cancellation token. /// Task{System.Boolean}. /// public abstract Task FetchAsync(BaseItem item, bool force, CancellationToken cancellationToken); /// /// Gets the priority. /// /// The priority. public abstract MetadataProviderPriority Priority { get; } /// /// Returns true or false indicating if the provider should refresh when the contents of it's directory changes /// /// true if [refresh on file system stamp change]; otherwise, false. protected virtual bool RefreshOnFileSystemStampChange { get { return false; } } /// /// Determines if the parent's file system stamp should be used for comparison /// /// The item. /// true if XXXX, false otherwise protected virtual bool UseParentFileSystemStamp(BaseItem item) { // True when the current item is just a file return !item.ResolveArgs.IsDirectory; } /// /// Gets the item's current file system stamp /// /// The item. /// Guid. private string GetCurrentFileSystemStamp(BaseItem item) { if (UseParentFileSystemStamp(item) && item.Parent != null) { return item.Parent.FileSystemStamp; } return item.FileSystemStamp; } } }