2019-01-13 13:03:10 -07:00
using System ;
2018-12-30 16:28:23 -07:00
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Threading ;
using System.Threading.Tasks ;
using MediaBrowser.Common.Net ;
using MediaBrowser.Common.Progress ;
2015-01-23 21:50:45 -07:00
using MediaBrowser.Controller ;
2013-03-03 22:43:06 -07:00
using MediaBrowser.Controller.Configuration ;
2018-12-30 16:28:23 -07:00
using MediaBrowser.Controller.Dto ;
2013-02-20 18:33:05 -07:00
using MediaBrowser.Controller.Entities ;
2014-02-02 06:36:31 -07:00
using MediaBrowser.Controller.Entities.Audio ;
using MediaBrowser.Controller.Entities.Movies ;
using MediaBrowser.Controller.Entities.TV ;
2013-06-23 10:48:30 -07:00
using MediaBrowser.Controller.Library ;
2013-03-07 22:08:27 -07:00
using MediaBrowser.Controller.Providers ;
2018-12-30 16:28:23 -07:00
using MediaBrowser.Controller.Subtitles ;
2014-02-02 06:36:31 -07:00
using MediaBrowser.Model.Configuration ;
2013-06-23 10:48:30 -07:00
using MediaBrowser.Model.Entities ;
2017-06-23 09:04:45 -07:00
using MediaBrowser.Model.Events ;
2018-12-30 16:28:23 -07:00
using MediaBrowser.Model.IO ;
using MediaBrowser.Model.Providers ;
2016-04-24 17:36:10 -07:00
using MediaBrowser.Model.Serialization ;
2018-12-30 16:28:23 -07:00
using Microsoft.Extensions.Logging ;
2017-04-29 19:37:51 -07:00
using Priority_Queue ;
2013-02-20 18:33:05 -07:00
2014-01-28 11:37:01 -07:00
namespace MediaBrowser.Providers.Manager
2013-02-20 18:33:05 -07:00
{
/// <summary>
/// Class ProviderManager
/// </summary>
2015-03-13 21:50:23 -07:00
public class ProviderManager : IProviderManager , IDisposable
2013-02-20 18:33:05 -07:00
{
2013-02-21 14:39:53 -07:00
/// <summary>
/// The _logger
/// </summary>
private readonly ILogger _logger ;
2013-02-24 17:13:45 -07:00
/// <summary>
/// The _HTTP client
/// </summary>
private readonly IHttpClient _httpClient ;
2013-03-07 22:08:27 -07:00
/// <summary>
/// The _directory watchers
/// </summary>
2014-01-28 14:25:10 -07:00
private readonly ILibraryMonitor _libraryMonitor ;
2013-03-07 22:08:27 -07:00
/// <summary>
/// Gets or sets the configuration manager.
/// </summary>
/// <value>The configuration manager.</value>
2013-03-03 22:43:06 -07:00
private IServerConfigurationManager ConfigurationManager { get ; set ; }
2013-03-06 22:34:00 -07:00
2013-10-30 14:33:27 -07:00
private IImageProvider [ ] ImageProviders { get ; set ; }
2014-01-28 11:37:01 -07:00
2013-10-31 07:03:23 -07:00
private readonly IFileSystem _fileSystem ;
2013-10-30 14:33:27 -07:00
2014-01-28 14:25:10 -07:00
private IMetadataService [ ] _metadataServices = { } ;
2014-01-31 12:55:21 -07:00
private IMetadataProvider [ ] _metadataProviders = { } ;
2014-02-02 06:36:31 -07:00
private IEnumerable < IMetadataSaver > _savers ;
2015-01-23 21:50:45 -07:00
private readonly IServerApplicationPaths _appPaths ;
2016-04-24 17:36:10 -07:00
private readonly IJsonSerializer _json ;
2014-01-28 11:37:01 -07:00
2014-02-21 11:48:15 -07:00
private IExternalId [ ] _externalIds ;
2015-03-13 21:50:23 -07:00
private readonly Func < ILibraryManager > _libraryManagerFactory ;
2017-06-05 23:13:49 -07:00
private CancellationTokenSource _disposeCancellationTokenSource = new CancellationTokenSource ( ) ;
2015-03-13 21:50:23 -07:00
2017-06-23 09:04:45 -07:00
public event EventHandler < GenericEventArgs < BaseItem > > RefreshStarted ;
public event EventHandler < GenericEventArgs < BaseItem > > RefreshCompleted ;
public event EventHandler < GenericEventArgs < Tuple < BaseItem , double > > > RefreshProgress ;
2018-09-12 10:26:21 -07:00
private ISubtitleManager _subtitleManager ;
2013-02-20 18:33:05 -07:00
/// <summary>
/// Initializes a new instance of the <see cref="ProviderManager" /> class.
/// </summary>
2018-12-13 06:18:25 -07:00
public ProviderManager ( IHttpClient httpClient , ISubtitleManager subtitleManager , IServerConfigurationManager configurationManager , ILibraryMonitor libraryMonitor , ILoggerFactory loggerFactory , IFileSystem fileSystem , IServerApplicationPaths appPaths , Func < ILibraryManager > libraryManagerFactory , IJsonSerializer json )
2013-02-20 18:33:05 -07:00
{
2018-12-13 06:18:25 -07:00
_logger = loggerFactory . CreateLogger ( "ProviderManager" ) ;
2013-02-24 17:13:45 -07:00
_httpClient = httpClient ;
2013-03-03 22:43:06 -07:00
ConfigurationManager = configurationManager ;
2014-01-28 14:25:10 -07:00
_libraryMonitor = libraryMonitor ;
2013-10-31 07:03:23 -07:00
_fileSystem = fileSystem ;
2015-01-23 21:50:45 -07:00
_appPaths = appPaths ;
2015-03-13 21:50:23 -07:00
_libraryManagerFactory = libraryManagerFactory ;
2016-04-24 17:36:10 -07:00
_json = json ;
2018-09-12 10:26:21 -07:00
_subtitleManager = subtitleManager ;
2013-02-20 18:33:05 -07:00
}
/// <summary>
2013-03-07 22:08:27 -07:00
/// Adds the metadata providers.
2013-02-20 18:33:05 -07:00
/// </summary>
2014-05-05 12:53:05 -07:00
public void AddParts ( IEnumerable < IImageProvider > imageProviders , IEnumerable < IMetadataService > metadataServices ,
IEnumerable < IMetadataProvider > metadataProviders , IEnumerable < IMetadataSaver > metadataSavers ,
2016-10-07 08:08:13 -07:00
IEnumerable < IExternalId > externalIds )
2013-02-20 18:33:05 -07:00
{
2014-01-31 12:55:21 -07:00
ImageProviders = imageProviders . ToArray ( ) ;
2014-01-28 11:37:01 -07:00
_metadataServices = metadataServices . OrderBy ( i = > i . Order ) . ToArray ( ) ;
2014-01-31 12:55:21 -07:00
_metadataProviders = metadataProviders . ToArray ( ) ;
2014-02-21 11:48:15 -07:00
_externalIds = externalIds . OrderBy ( i = > i . Name ) . ToArray ( ) ;
2015-08-02 12:08:55 -07:00
_savers = metadataSavers . Where ( i = >
{
var configurable = i as IConfigurableProvider ;
return configurable = = null | | configurable . IsEnabled ;
} ) . ToArray ( ) ;
2014-01-28 11:37:01 -07:00
}
2018-09-12 10:26:21 -07:00
public Task < ItemUpdateType > RefreshSingleItem ( BaseItem item , MetadataRefreshOptions options , CancellationToken cancellationToken )
2014-01-28 11:37:01 -07:00
{
2017-10-06 08:49:22 -07:00
IMetadataService service = null ;
var type = item . GetType ( ) ;
foreach ( var current in _metadataServices )
{
if ( current . CanRefreshPrimary ( type ) )
{
service = current ;
break ;
}
}
if ( service = = null )
{
foreach ( var current in _metadataServices )
{
if ( current . CanRefresh ( item ) )
{
service = current ;
break ;
}
}
}
2014-01-28 11:37:01 -07:00
if ( service ! = null )
{
return service . RefreshMetadata ( item , options , cancellationToken ) ;
}
2018-12-20 05:11:26 -07:00
_logger . LogError ( "Unable to find a metadata service for item of type {TypeName}" , item . GetType ( ) . Name ) ;
2015-03-10 19:07:07 -07:00
return Task . FromResult ( ItemUpdateType . None ) ;
2013-02-20 18:33:05 -07:00
}
2018-09-12 10:26:21 -07:00
public async Task SaveImage ( BaseItem item , string url , ImageType type , int? imageIndex , CancellationToken cancellationToken )
2013-06-28 13:25:58 -07:00
{
2017-10-20 09:16:56 -07:00
using ( var response = await _httpClient . GetResponse ( new HttpRequestOptions
2013-06-28 13:25:58 -07:00
{
CancellationToken = cancellationToken ,
2016-10-31 11:39:41 -07:00
Url = url ,
BufferContent = false
2013-06-28 13:25:58 -07:00
2017-10-20 09:16:56 -07:00
} ) . ConfigureAwait ( false ) )
{
2019-01-07 16:24:34 -07:00
// Workaround for tvheadend channel icons
2018-09-12 10:26:21 -07:00
// TODO: Isolate this hack into the tvh plugin
if ( string . IsNullOrEmpty ( response . ContentType ) )
{
if ( url . IndexOf ( "/imagecache/" , StringComparison . OrdinalIgnoreCase ) ! = - 1 )
{
response . ContentType = "image/png" ;
}
}
2017-10-20 09:16:56 -07:00
await SaveImage ( item , response . Content , response . ContentType , type , imageIndex , cancellationToken ) . ConfigureAwait ( false ) ;
}
2013-06-28 13:25:58 -07:00
}
2018-09-12 10:26:21 -07:00
public Task SaveImage ( BaseItem item , Stream source , string mimeType , ImageType type , int? imageIndex , CancellationToken cancellationToken )
2013-06-28 13:25:58 -07:00
{
2018-09-12 10:26:21 -07:00
return new ImageSaver ( ConfigurationManager , _libraryMonitor , _fileSystem , _logger ) . SaveImage ( item , source , mimeType , type , imageIndex , cancellationToken ) ;
2013-06-28 13:25:58 -07:00
}
2013-10-30 14:33:27 -07:00
2018-09-12 10:26:21 -07:00
public Task SaveImage ( BaseItem item , string source , string mimeType , ImageType type , int? imageIndex , bool? saveLocallyWithMedia , CancellationToken cancellationToken )
2015-04-08 08:45:30 -07:00
{
2015-06-21 14:31:21 -07:00
if ( string . IsNullOrWhiteSpace ( source ) )
{
2019-01-06 13:50:43 -07:00
throw new ArgumentNullException ( nameof ( source ) ) ;
2015-06-21 14:31:21 -07:00
}
2016-10-25 12:02:04 -07:00
var fileStream = _fileSystem . GetFileStream ( source , FileOpenMode . Open , FileAccessMode . Read , FileShareMode . ReadWrite , true ) ;
2015-04-08 08:45:30 -07:00
2018-09-12 10:26:21 -07:00
return new ImageSaver ( ConfigurationManager , _libraryMonitor , _fileSystem , _logger ) . SaveImage ( item , fileStream , mimeType , type , imageIndex , saveLocallyWithMedia , cancellationToken ) ;
2015-04-08 08:45:30 -07:00
}
2018-09-12 10:26:21 -07:00
public async Task < IEnumerable < RemoteImageInfo > > GetAvailableRemoteImages ( BaseItem item , RemoteImageQuery query , CancellationToken cancellationToken )
2013-10-30 14:33:27 -07:00
{
2014-02-11 20:46:27 -07:00
var providers = GetRemoteImageProviders ( item , query . IncludeDisabledProviders ) ;
2013-10-31 18:48:14 -07:00
2014-02-11 20:46:27 -07:00
if ( ! string . IsNullOrEmpty ( query . ProviderName ) )
2013-10-31 18:48:14 -07:00
{
2014-02-11 20:46:27 -07:00
var providerName = query . ProviderName ;
2013-10-31 18:48:14 -07:00
providers = providers . Where ( i = > string . Equals ( i . Name , providerName , StringComparison . OrdinalIgnoreCase ) ) ;
}
2013-12-26 17:23:58 -07:00
var preferredLanguage = item . GetPreferredMetadataLanguage ( ) ;
2013-10-30 14:33:27 -07:00
2014-06-23 21:18:02 -07:00
var languages = new List < string > ( ) ;
if ( ! query . IncludeAllLanguages & & ! string . IsNullOrWhiteSpace ( preferredLanguage ) )
{
languages . Add ( preferredLanguage ) ;
}
2014-02-11 20:46:27 -07:00
2014-06-23 21:18:02 -07:00
var tasks = providers . Select ( i = > GetImages ( item , cancellationToken , i , languages , query . ImageType ) ) ;
2013-12-29 19:41:22 -07:00
var results = await Task . WhenAll ( tasks ) . ConfigureAwait ( false ) ;
2017-08-09 12:56:38 -07:00
return results . SelectMany ( i = > i . ToList ( ) ) ;
2013-12-29 19:41:22 -07:00
}
/// <summary>
/// Gets the images.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="cancellationToken">The cancellation token.</param>
2014-02-12 22:11:54 -07:00
/// <param name="provider">The provider.</param>
2014-06-23 21:18:02 -07:00
/// <param name="preferredLanguages">The preferred languages.</param>
2013-12-29 19:41:22 -07:00
/// <param name="type">The type.</param>
/// <returns>Task{IEnumerable{RemoteImageInfo}}.</returns>
2018-09-12 10:26:21 -07:00
private async Task < IEnumerable < RemoteImageInfo > > GetImages ( BaseItem item , CancellationToken cancellationToken , IRemoteImageProvider provider , List < string > preferredLanguages , ImageType ? type = null )
2013-12-29 19:41:22 -07:00
{
try
2013-10-30 14:33:27 -07:00
{
2014-02-12 22:11:54 -07:00
var result = await provider . GetImages ( item , cancellationToken ) . ConfigureAwait ( false ) ;
2013-12-29 19:41:22 -07:00
if ( type . HasValue )
2013-10-30 14:33:27 -07:00
{
2014-02-12 22:11:54 -07:00
result = result . Where ( i = > i . Type = = type . Value ) ;
2013-10-30 14:33:27 -07:00
}
2014-02-11 20:46:27 -07:00
2014-06-23 21:18:02 -07:00
if ( preferredLanguages . Count > 0 )
2014-02-18 22:21:03 -07:00
{
result = result . Where ( i = > string . IsNullOrEmpty ( i . Language ) | |
2014-07-04 21:01:30 -07:00
preferredLanguages . Contains ( i . Language , StringComparer . OrdinalIgnoreCase ) | |
string . Equals ( i . Language , "en" , StringComparison . OrdinalIgnoreCase ) ) ;
2014-02-18 22:21:03 -07:00
}
return result ;
2013-12-29 19:41:22 -07:00
}
2014-02-25 21:38:21 -07:00
catch ( OperationCanceledException )
{
return new List < RemoteImageInfo > ( ) ;
}
2013-12-29 19:41:22 -07:00
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "{0} failed in GetImageInfos for type {1}" , provider . GetType ( ) . Name , item . GetType ( ) . Name ) ;
2013-12-29 19:41:22 -07:00
return new List < RemoteImageInfo > ( ) ;
}
2013-10-30 14:33:27 -07:00
}
2014-01-31 12:55:21 -07:00
/// <summary>
/// Gets the supported image providers.
/// </summary>
/// <param name="item">The item.</param>
/// <returns>IEnumerable{IImageProvider}.</returns>
2018-09-12 10:26:21 -07:00
public IEnumerable < ImageProviderInfo > GetRemoteImageProviderInfo ( BaseItem item )
2014-01-31 12:55:21 -07:00
{
2014-02-10 21:55:01 -07:00
return GetRemoteImageProviders ( item , true ) . Select ( i = > new ImageProviderInfo
2014-01-31 12:55:21 -07:00
{
2014-02-10 21:55:01 -07:00
Name = i . Name ,
2017-08-19 12:43:35 -07:00
SupportedImages = i . GetSupportedImages ( item ) . ToArray ( )
2014-01-31 12:55:21 -07:00
} ) ;
}
2018-09-12 10:26:21 -07:00
public IEnumerable < IImageProvider > GetImageProviders ( BaseItem item , ImageRefreshOptions refreshOptions )
2013-10-30 14:33:27 -07:00
{
2018-09-12 10:26:21 -07:00
return GetImageProviders ( item , _libraryManagerFactory ( ) . GetLibraryOptions ( item ) , GetMetadataOptions ( item ) , refreshOptions , false ) ;
2014-02-10 21:55:01 -07:00
}
2018-09-12 10:26:21 -07:00
private IEnumerable < IImageProvider > GetImageProviders ( BaseItem item , LibraryOptions libraryOptions , MetadataOptions options , ImageRefreshOptions refreshOptions , bool includeDisabled )
2014-02-10 21:55:01 -07:00
{
2014-02-18 22:21:03 -07:00
// Avoid implicitly captured closure
var currentOptions = options ;
2018-09-12 10:26:21 -07:00
var typeOptions = libraryOptions . GetTypeOptions ( item . GetType ( ) . Name ) ;
2019-08-29 00:14:50 -07:00
var typeFetcherOrder = typeOptions ? . ImageFetcherOrder ;
2014-02-10 21:55:01 -07:00
2018-09-12 10:26:21 -07:00
return ImageProviders . Where ( i = > CanRefresh ( i , item , libraryOptions , options , refreshOptions , includeDisabled ) )
. OrderBy ( i = >
{
// See if there's a user-defined order
if ( ! ( i is ILocalImageProvider ) )
2014-02-10 21:55:01 -07:00
{
2018-09-12 10:26:21 -07:00
var fetcherOrder = typeFetcherOrder ? ? currentOptions . ImageFetcherOrder ;
var index = Array . IndexOf ( fetcherOrder , i . Name ) ;
if ( index ! = - 1 )
{
return index ;
}
2014-02-10 21:55:01 -07:00
}
2014-01-28 11:37:01 -07:00
2018-09-12 10:26:21 -07:00
// Not configured. Just return some high number to put it at the end.
return 100 ;
} )
2014-02-10 21:55:01 -07:00
. ThenBy ( GetOrder ) ;
2014-01-31 12:55:21 -07:00
}
2018-09-12 10:26:21 -07:00
public IEnumerable < IMetadataProvider < T > > GetMetadataProviders < T > ( BaseItem item , LibraryOptions libraryOptions )
where T : BaseItem
2014-02-02 06:36:31 -07:00
{
2018-09-12 10:26:21 -07:00
var globalMetadataOptions = GetMetadataOptions ( item ) ;
2014-02-10 11:39:41 -07:00
2018-09-12 10:26:21 -07:00
return GetMetadataProvidersInternal < T > ( item , libraryOptions , globalMetadataOptions , false , false ) ;
2014-02-02 06:36:31 -07:00
}
2018-09-12 10:26:21 -07:00
private IEnumerable < IMetadataProvider < T > > GetMetadataProvidersInternal < T > ( BaseItem item , LibraryOptions libraryOptions , MetadataOptions globalMetadataOptions , bool includeDisabled , bool forceEnableInternetMetadata )
where T : BaseItem
2014-01-31 12:55:21 -07:00
{
2014-02-18 22:21:03 -07:00
// Avoid implicitly captured closure
2018-09-12 10:26:21 -07:00
var currentOptions = globalMetadataOptions ;
2014-02-11 20:46:27 -07:00
2014-02-18 22:21:03 -07:00
return _metadataProviders . OfType < IMetadataProvider < T > > ( )
2018-09-12 10:26:21 -07:00
. Where ( i = > CanRefresh ( i , item , libraryOptions , currentOptions , includeDisabled , forceEnableInternetMetadata ) )
. OrderBy ( i = > GetConfiguredOrder ( item , i , libraryOptions , globalMetadataOptions ) )
2014-02-18 22:21:03 -07:00
. ThenBy ( GetDefaultOrder ) ;
2014-01-31 12:55:21 -07:00
}
2018-09-12 10:26:21 -07:00
private IEnumerable < IRemoteImageProvider > GetRemoteImageProviders ( BaseItem item , bool includeDisabled )
2014-01-31 12:55:21 -07:00
{
2014-02-10 21:55:01 -07:00
var options = GetMetadataOptions ( item ) ;
2018-09-12 10:26:21 -07:00
var libraryOptions = _libraryManagerFactory ( ) . GetLibraryOptions ( item ) ;
2014-02-10 21:55:01 -07:00
2018-12-14 12:17:29 -07:00
return GetImageProviders ( item , libraryOptions , options ,
new ImageRefreshOptions (
new DirectoryService ( _logger , _fileSystem ) ) ,
includeDisabled )
. OfType < IRemoteImageProvider > ( ) ;
2014-01-28 11:37:01 -07:00
}
2018-09-12 10:26:21 -07:00
private bool CanRefresh ( IMetadataProvider provider , BaseItem item , LibraryOptions libraryOptions , MetadataOptions options , bool includeDisabled , bool forceEnableInternetMetadata )
2014-01-28 11:37:01 -07:00
{
2014-02-10 21:55:01 -07:00
if ( ! includeDisabled )
2014-01-28 11:37:01 -07:00
{
2014-02-18 22:21:03 -07:00
// If locked only allow local providers
if ( item . IsLocked & & ! ( provider is ILocalMetadataProvider ) & & ! ( provider is IForcedProvider ) )
{
return false ;
}
2014-02-10 21:55:01 -07:00
if ( provider is IRemoteMetadataProvider )
{
2018-09-12 10:26:21 -07:00
if ( ! forceEnableInternetMetadata & & ! item . IsMetadataFetcherEnabled ( libraryOptions , provider . Name ) )
2014-02-10 21:55:01 -07:00
{
return false ;
}
}
2014-01-31 12:55:21 -07:00
}
2014-01-28 11:37:01 -07:00
2014-02-10 13:11:46 -07:00
if ( ! item . SupportsLocalMetadata & & provider is ILocalMetadataProvider )
2014-01-31 12:55:21 -07:00
{
return false ;
}
2014-02-05 21:39:16 -07:00
// If this restriction is ever lifted, movie xml providers will have to be updated to prevent owned items like trailers from reading those files
2018-09-12 10:26:21 -07:00
if ( ! item . OwnerId . Equals ( Guid . Empty ) )
2014-02-05 21:39:16 -07:00
{
if ( provider is ILocalMetadataProvider | | provider is IRemoteMetadataProvider )
{
return false ;
}
}
2014-01-31 12:55:21 -07:00
return true ;
}
2018-09-12 10:26:21 -07:00
private bool CanRefresh ( IImageProvider provider , BaseItem item , LibraryOptions libraryOptions , MetadataOptions options , ImageRefreshOptions refreshOptions , bool includeDisabled )
2014-02-10 21:55:01 -07:00
{
if ( ! includeDisabled )
{
2014-02-18 22:21:03 -07:00
// If locked only allow local providers
if ( item . IsLocked & & ! ( provider is ILocalImageProvider ) )
{
2018-09-12 10:26:21 -07:00
if ( refreshOptions . ImageRefreshMode ! = MetadataRefreshMode . FullRefresh )
2017-01-21 13:27:07 -07:00
{
return false ;
}
2014-02-18 22:21:03 -07:00
}
2014-02-19 09:24:06 -07:00
2014-06-15 16:30:04 -07:00
if ( provider is IRemoteImageProvider | | provider is IDynamicImageProvider )
2014-02-10 21:55:01 -07:00
{
2018-09-12 10:26:21 -07:00
if ( ! item . IsImageFetcherEnabled ( libraryOptions , provider . Name ) )
2014-02-26 20:57:37 -07:00
{
return false ;
}
2014-02-10 21:55:01 -07:00
}
}
try
{
return provider . Supports ( item ) ;
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "{0} failed in Supports for type {1}" , provider . GetType ( ) . Name , item . GetType ( ) . Name ) ;
2014-02-10 21:55:01 -07:00
return false ;
}
}
2014-01-31 12:55:21 -07:00
/// <summary>
/// Gets the order.
/// </summary>
/// <param name="provider">The provider.</param>
/// <returns>System.Int32.</returns>
2014-02-10 11:39:41 -07:00
private int GetOrder ( IImageProvider provider )
2014-01-31 12:55:21 -07:00
{
var hasOrder = provider as IHasOrder ;
if ( hasOrder = = null )
{
return 0 ;
}
return hasOrder . Order ;
}
2018-09-12 10:26:21 -07:00
private int GetConfiguredOrder ( BaseItem item , IMetadataProvider provider , LibraryOptions libraryOptions , MetadataOptions globalMetadataOptions )
2014-02-18 22:21:03 -07:00
{
// See if there's a user-defined order
if ( provider is ILocalMetadataProvider )
{
2018-09-12 10:26:21 -07:00
var configuredOrder = libraryOptions . LocalMetadataReaderOrder ? ? globalMetadataOptions . LocalMetadataReaderOrder ;
var index = Array . IndexOf ( configuredOrder , provider . Name ) ;
2014-02-18 22:21:03 -07:00
if ( index ! = - 1 )
{
return index ;
}
}
// See if there's a user-defined order
if ( provider is IRemoteMetadataProvider )
{
2018-09-12 10:26:21 -07:00
var typeOptions = libraryOptions . GetTypeOptions ( item . GetType ( ) . Name ) ;
var typeFetcherOrder = typeOptions = = null ? null : typeOptions . MetadataFetcherOrder ;
var fetcherOrder = typeFetcherOrder ? ? globalMetadataOptions . MetadataFetcherOrder ;
var index = Array . IndexOf ( fetcherOrder , provider . Name ) ;
2014-02-18 22:21:03 -07:00
if ( index ! = - 1 )
{
return index ;
}
}
// Not configured. Just return some high number to put it at the end.
return 100 ;
}
2014-02-19 09:24:06 -07:00
2014-02-18 22:21:03 -07:00
private int GetDefaultOrder ( IMetadataProvider provider )
2014-01-31 12:55:21 -07:00
{
var hasOrder = provider as IHasOrder ;
2014-02-10 11:39:41 -07:00
if ( hasOrder ! = null )
2014-01-31 12:55:21 -07:00
{
2014-02-10 11:39:41 -07:00
return hasOrder . Order ;
2014-01-31 12:55:21 -07:00
}
2014-02-10 11:39:41 -07:00
return 0 ;
2013-10-30 14:33:27 -07:00
}
2014-02-02 06:36:31 -07:00
2017-08-19 12:43:35 -07:00
public MetadataPluginSummary [ ] GetAllMetadataPlugins ( )
2014-02-02 06:36:31 -07:00
{
2017-08-19 12:43:35 -07:00
return new MetadataPluginSummary [ ]
2014-02-07 13:30:41 -07:00
{
GetPluginSummary < Movie > ( ) ,
GetPluginSummary < BoxSet > ( ) ,
GetPluginSummary < Book > ( ) ,
GetPluginSummary < Series > ( ) ,
GetPluginSummary < Season > ( ) ,
GetPluginSummary < Episode > ( ) ,
GetPluginSummary < MusicAlbum > ( ) ,
GetPluginSummary < MusicArtist > ( ) ,
GetPluginSummary < Audio > ( ) ,
2017-10-06 08:49:22 -07:00
GetPluginSummary < AudioBook > ( ) ,
2014-02-07 13:30:41 -07:00
GetPluginSummary < Studio > ( ) ,
GetPluginSummary < MusicVideo > ( ) ,
2018-09-12 10:26:21 -07:00
GetPluginSummary < Video > ( )
2014-02-07 13:30:41 -07:00
} ;
2014-02-02 06:36:31 -07:00
}
private MetadataPluginSummary GetPluginSummary < T > ( )
where T : BaseItem , new ( )
{
// Give it a dummy path just so that it looks like a file system item
var dummy = new T ( )
{
2015-09-06 09:02:41 -07:00
Path = Path . Combine ( _appPaths . InternalMetadataPath , "dummy" ) ,
ParentId = Guid . NewGuid ( )
2014-02-02 06:36:31 -07:00
} ;
2014-02-10 11:39:41 -07:00
var options = GetMetadataOptions ( dummy ) ;
2014-02-02 06:36:31 -07:00
var summary = new MetadataPluginSummary
{
ItemType = typeof ( T ) . Name
} ;
2018-09-12 10:26:21 -07:00
var libraryOptions = new LibraryOptions ( ) ;
2018-12-14 12:17:29 -07:00
var imageProviders = GetImageProviders ( dummy , libraryOptions , options ,
new ImageRefreshOptions (
new DirectoryService ( _logger , _fileSystem ) ) ,
true )
. ToList ( ) ;
2014-02-02 06:36:31 -07:00
2017-08-19 12:43:35 -07:00
var pluginList = summary . Plugins . ToList ( ) ;
2018-09-12 10:26:21 -07:00
AddMetadataPlugins ( pluginList , dummy , libraryOptions , options ) ;
2017-08-19 12:43:35 -07:00
AddImagePlugins ( pluginList , dummy , imageProviders ) ;
2018-09-12 10:26:21 -07:00
var subtitleProviders = _subtitleManager . GetSupportedProviders ( dummy ) ;
// Subtitle fetchers
pluginList . AddRange ( subtitleProviders . Select ( i = > new MetadataPlugin
{
Name = i . Name ,
Type = MetadataPluginType . SubtitleFetcher
} ) ) ;
2018-12-28 08:48:26 -07:00
summary . Plugins = pluginList . ToArray ( ) ;
2014-02-02 06:36:31 -07:00
2014-02-23 20:27:13 -07:00
var supportedImageTypes = imageProviders . OfType < IRemoteImageProvider > ( )
2014-02-02 06:36:31 -07:00
. SelectMany ( i = > i . GetSupportedImages ( dummy ) )
. ToList ( ) ;
2014-02-23 20:27:13 -07:00
supportedImageTypes . AddRange ( imageProviders . OfType < IDynamicImageProvider > ( )
. SelectMany ( i = > i . GetSupportedImages ( dummy ) ) ) ;
2017-08-19 12:43:35 -07:00
summary . SupportedImageTypes = supportedImageTypes . Distinct ( ) . ToArray ( ) ;
2014-02-02 06:36:31 -07:00
return summary ;
}
2018-09-12 10:26:21 -07:00
private void AddMetadataPlugins < T > ( List < MetadataPlugin > list , T item , LibraryOptions libraryOptions , MetadataOptions options )
where T : BaseItem
2014-02-02 06:36:31 -07:00
{
2018-09-12 10:26:21 -07:00
var providers = GetMetadataProvidersInternal < T > ( item , libraryOptions , options , true , true ) . ToList ( ) ;
2014-02-02 06:36:31 -07:00
// Locals
list . AddRange ( providers . Where ( i = > ( i is ILocalMetadataProvider ) ) . Select ( i = > new MetadataPlugin
{
Name = i . Name ,
Type = MetadataPluginType . LocalMetadataProvider
} ) ) ;
2016-12-29 00:14:59 -07:00
// Fetchers
list . AddRange ( providers . Where ( i = > ( i is IRemoteMetadataProvider ) ) . Select ( i = > new MetadataPlugin
2014-02-02 06:36:31 -07:00
{
2016-12-29 00:14:59 -07:00
Name = i . Name ,
Type = MetadataPluginType . MetadataFetcher
} ) ) ;
2014-03-02 08:42:21 -07:00
2016-12-29 00:14:59 -07:00
// Savers
2018-09-12 10:26:21 -07:00
list . AddRange ( _savers . Where ( i = > IsSaverEnabledForItem ( i , item , libraryOptions , ItemUpdateType . MetadataEdit , true ) ) . OrderBy ( i = > i . Name ) . Select ( i = > new MetadataPlugin
2014-02-02 06:36:31 -07:00
{
2016-12-29 00:14:59 -07:00
Name = i . Name ,
Type = MetadataPluginType . MetadataSaver
} ) ) ;
2014-02-02 06:36:31 -07:00
}
private void AddImagePlugins < T > ( List < MetadataPlugin > list , T item , List < IImageProvider > imageProviders )
2018-09-12 10:26:21 -07:00
where T : BaseItem
2014-02-02 06:36:31 -07:00
{
// Locals
list . AddRange ( imageProviders . Where ( i = > ( i is ILocalImageProvider ) ) . Select ( i = > new MetadataPlugin
{
Name = i . Name ,
Type = MetadataPluginType . LocalImageProvider
} ) ) ;
// Fetchers
2018-09-12 10:26:21 -07:00
list . AddRange ( imageProviders . Where ( i = > i is IDynamicImageProvider | | ( i is IRemoteImageProvider ) ) . Select ( i = > new MetadataPlugin
2014-02-02 06:36:31 -07:00
{
Name = i . Name ,
Type = MetadataPluginType . ImageFetcher
} ) ) ;
}
2018-09-12 10:26:21 -07:00
public MetadataOptions GetMetadataOptions ( BaseItem item )
2014-02-09 16:08:01 -07:00
{
var type = item . GetType ( ) . Name ;
2014-02-10 11:39:41 -07:00
2014-02-09 16:08:01 -07:00
return ConfigurationManager . Configuration . MetadataOptions
. FirstOrDefault ( i = > string . Equals ( i . ItemType , type , StringComparison . OrdinalIgnoreCase ) ) ? ?
new MetadataOptions ( ) ;
}
2014-02-10 11:39:41 -07:00
2014-02-02 06:36:31 -07:00
/// <summary>
/// Saves the metadata.
/// </summary>
2018-09-12 10:26:21 -07:00
public void SaveMetadata ( BaseItem item , ItemUpdateType updateType )
2014-02-02 06:36:31 -07:00
{
2017-05-25 23:48:54 -07:00
SaveMetadata ( item , updateType , _savers ) ;
2014-09-26 19:28:13 -07:00
}
/// <summary>
/// Saves the metadata.
/// </summary>
2018-09-12 10:26:21 -07:00
public void SaveMetadata ( BaseItem item , ItemUpdateType updateType , IEnumerable < string > savers )
2014-09-26 19:28:13 -07:00
{
2017-05-25 23:48:54 -07:00
SaveMetadata ( item , updateType , _savers . Where ( i = > savers . Contains ( i . Name , StringComparer . OrdinalIgnoreCase ) ) ) ;
2014-09-26 19:28:13 -07:00
}
/// <summary>
/// Saves the metadata.
/// </summary>
/// <param name="item">The item.</param>
/// <param name="updateType">Type of the update.</param>
/// <param name="savers">The savers.</param>
/// <returns>Task.</returns>
2018-09-12 10:26:21 -07:00
private void SaveMetadata ( BaseItem item , ItemUpdateType updateType , IEnumerable < IMetadataSaver > savers )
2014-09-26 19:28:13 -07:00
{
2018-09-12 10:26:21 -07:00
var libraryOptions = _libraryManagerFactory ( ) . GetLibraryOptions ( item ) ;
foreach ( var saver in savers . Where ( i = > IsSaverEnabledForItem ( i , item , libraryOptions , updateType , false ) ) )
2014-02-02 06:36:31 -07:00
{
2018-12-13 06:18:25 -07:00
_logger . LogDebug ( "Saving {0} to {1}." , item . Path ? ? item . Name , saver . Name ) ;
2014-02-10 11:39:41 -07:00
2014-02-02 09:59:14 -07:00
var fileSaver = saver as IMetadataFileSaver ;
2014-02-02 06:36:31 -07:00
2014-02-02 09:59:14 -07:00
if ( fileSaver ! = null )
{
2014-02-08 13:02:35 -07:00
string path = null ;
try
2014-02-02 09:59:14 -07:00
{
2014-02-08 13:02:35 -07:00
path = fileSaver . GetSavePath ( item ) ;
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error in {0} GetSavePath" , saver . Name ) ;
2014-02-08 13:02:35 -07:00
continue ;
2014-02-02 09:59:14 -07:00
}
2014-02-02 06:36:31 -07:00
2014-02-02 09:59:14 -07:00
try
{
_libraryMonitor . ReportFileSystemChangeBeginning ( path ) ;
saver . Save ( item , CancellationToken . None ) ;
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error in metadata saver" ) ;
2014-02-02 09:59:14 -07:00
}
finally
{
_libraryMonitor . ReportFileSystemChangeComplete ( path , false ) ;
}
2014-02-02 06:36:31 -07:00
}
2014-02-02 09:59:14 -07:00
else
2014-02-02 06:36:31 -07:00
{
2014-02-02 09:59:14 -07:00
try
{
saver . Save ( item , CancellationToken . None ) ;
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error in metadata saver" ) ;
2014-02-02 09:59:14 -07:00
}
2014-02-02 06:36:31 -07:00
}
}
}
2014-02-08 13:02:35 -07:00
2014-09-26 19:28:13 -07:00
/// <summary>
/// Determines whether [is saver enabled for item] [the specified saver].
/// </summary>
2018-09-12 10:26:21 -07:00
private bool IsSaverEnabledForItem ( IMetadataSaver saver , BaseItem item , LibraryOptions libraryOptions , ItemUpdateType updateType , bool includeDisabled )
2014-02-08 13:02:35 -07:00
{
2014-02-09 16:08:01 -07:00
var options = GetMetadataOptions ( item ) ;
2014-02-08 13:02:35 -07:00
try
{
2018-09-12 10:26:21 -07:00
if ( ! saver . IsEnabledFor ( item , updateType ) )
{
return false ;
}
2014-06-03 20:34:36 -07:00
2014-02-26 20:57:37 -07:00
if ( ! includeDisabled )
2014-02-09 16:08:01 -07:00
{
2018-09-12 10:26:21 -07:00
if ( libraryOptions . MetadataSavers = = null )
2014-02-26 20:57:37 -07:00
{
2018-09-12 10:26:21 -07:00
if ( options . DisabledMetadataSavers . Contains ( saver . Name , StringComparer . OrdinalIgnoreCase ) )
{
return false ;
}
2014-06-03 20:34:36 -07:00
2018-09-12 10:26:21 -07:00
if ( ! item . IsSaveLocalMetadataEnabled ( ) )
2014-04-07 21:17:18 -07:00
{
2018-09-12 10:26:21 -07:00
if ( updateType > = ItemUpdateType . MetadataEdit )
{
var fileSaver = saver as IMetadataFileSaver ;
2014-04-07 21:17:18 -07:00
2018-09-12 10:26:21 -07:00
// Manual edit occurred
// Even if save local is off, save locally anyway if the metadata file already exists
2019-01-26 14:59:53 -07:00
if ( fileSaver = = null | | ! File . Exists ( fileSaver . GetSavePath ( item ) ) )
2018-09-12 10:26:21 -07:00
{
return false ;
}
}
else
2014-04-07 21:17:18 -07:00
{
2018-09-12 10:26:21 -07:00
// Manual edit did not occur
// Since local metadata saving is disabled, consider it disabled
2014-04-07 21:17:18 -07:00
return false ;
}
}
2018-09-12 10:26:21 -07:00
}
else
{
if ( ! libraryOptions . MetadataSavers . Contains ( saver . Name , StringComparer . OrdinalIgnoreCase ) )
2014-04-07 21:17:18 -07:00
{
return false ;
}
2014-02-26 20:57:37 -07:00
}
2014-02-09 16:08:01 -07:00
}
2018-09-12 10:26:21 -07:00
return true ;
2014-02-08 13:02:35 -07:00
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error in {0}.IsEnabledFor" , saver . Name ) ;
2014-02-08 13:02:35 -07:00
return false ;
}
}
2014-02-18 22:21:03 -07:00
2018-09-12 10:26:21 -07:00
public Task < IEnumerable < RemoteSearchResult > > GetRemoteSearchResults < TItemType , TLookupType > ( RemoteSearchQuery < TLookupType > searchInfo , CancellationToken cancellationToken )
2014-02-19 09:24:06 -07:00
where TItemType : BaseItem , new ( )
where TLookupType : ItemLookupInfo
{
2018-09-12 10:26:21 -07:00
BaseItem referenceItem = null ;
if ( ! searchInfo . ItemId . Equals ( Guid . Empty ) )
2014-02-19 09:24:06 -07:00
{
2018-09-12 10:26:21 -07:00
referenceItem = _libraryManagerFactory ( ) . GetItemById ( searchInfo . ItemId ) ;
}
2014-02-19 09:24:06 -07:00
2018-09-12 10:26:21 -07:00
return GetRemoteSearchResults < TItemType , TLookupType > ( searchInfo , referenceItem , cancellationToken ) ;
}
2015-07-08 22:52:25 -07:00
2018-09-12 10:26:21 -07:00
public async Task < IEnumerable < RemoteSearchResult > > GetRemoteSearchResults < TItemType , TLookupType > ( RemoteSearchQuery < TLookupType > searchInfo , BaseItem referenceItem , CancellationToken cancellationToken )
where TItemType : BaseItem , new ( )
where TLookupType : ItemLookupInfo
{
LibraryOptions libraryOptions ;
2014-02-19 09:24:06 -07:00
2018-09-12 10:26:21 -07:00
if ( referenceItem = = null )
{
// Give it a dummy path just so that it looks like a file system item
var dummy = new TItemType
{
Path = Path . Combine ( _appPaths . InternalMetadataPath , "dummy" ) ,
ParentId = Guid . NewGuid ( )
} ;
dummy . SetParent ( new Folder ( ) ) ;
referenceItem = dummy ;
libraryOptions = new LibraryOptions ( ) ;
}
else
{
libraryOptions = _libraryManagerFactory ( ) . GetLibraryOptions ( referenceItem ) ;
}
var options = GetMetadataOptions ( referenceItem ) ;
var providers = GetMetadataProvidersInternal < TItemType > ( referenceItem , libraryOptions , options , searchInfo . IncludeDisabledProviders , false )
2014-02-19 09:24:06 -07:00
. OfType < IRemoteSearchProvider < TLookupType > > ( ) ;
2014-02-18 22:21:03 -07:00
2014-02-19 09:24:06 -07:00
if ( ! string . IsNullOrEmpty ( searchInfo . SearchProviderName ) )
{
providers = providers . Where ( i = > string . Equals ( i . Name , searchInfo . SearchProviderName , StringComparison . OrdinalIgnoreCase ) ) ;
}
2014-03-01 15:34:27 -07:00
if ( string . IsNullOrWhiteSpace ( searchInfo . SearchInfo . MetadataLanguage ) )
{
searchInfo . SearchInfo . MetadataLanguage = ConfigurationManager . Configuration . PreferredMetadataLanguage ;
}
if ( string . IsNullOrWhiteSpace ( searchInfo . SearchInfo . MetadataCountryCode ) )
{
searchInfo . SearchInfo . MetadataCountryCode = ConfigurationManager . Configuration . MetadataCountryCode ;
}
2015-09-30 17:36:46 -07:00
var resultList = new List < RemoteSearchResult > ( ) ;
2014-02-19 09:24:06 -07:00
foreach ( var provider in providers )
{
2014-11-04 20:41:14 -07:00
try
{
var results = await GetSearchResults ( provider , searchInfo . SearchInfo , cancellationToken ) . ConfigureAwait ( false ) ;
2014-02-19 09:24:06 -07:00
2015-10-05 15:03:58 -07:00
foreach ( var result in results )
2014-11-04 20:41:14 -07:00
{
2016-04-24 17:36:10 -07:00
var existingMatch = resultList . FirstOrDefault ( i = > i . ProviderIds . Any ( p = > string . Equals ( result . GetProviderId ( p . Key ) , p . Value , StringComparison . OrdinalIgnoreCase ) ) ) ;
2015-09-30 17:36:46 -07:00
2016-04-24 17:36:10 -07:00
if ( existingMatch = = null )
2015-10-05 15:03:58 -07:00
{
2016-04-24 17:36:10 -07:00
resultList . Add ( result ) ;
}
else
{
foreach ( var providerId in result . ProviderIds )
2015-10-05 15:03:58 -07:00
{
2016-04-24 17:36:10 -07:00
if ( ! existingMatch . ProviderIds . ContainsKey ( providerId . Key ) )
2015-10-05 15:03:58 -07:00
{
2016-04-24 17:36:10 -07:00
existingMatch . ProviderIds . Add ( providerId . Key , providerId . Value ) ;
2015-10-05 15:03:58 -07:00
}
}
2016-04-24 17:36:10 -07:00
if ( string . IsNullOrWhiteSpace ( existingMatch . ImageUrl ) )
{
existingMatch . ImageUrl = result . ImageUrl ;
}
2015-10-05 15:03:58 -07:00
}
2014-11-04 20:41:14 -07:00
}
}
2018-12-30 16:28:23 -07:00
catch ( Exception )
2014-02-19 09:24:06 -07:00
{
2014-11-04 20:41:14 -07:00
// Logged at lower levels
2014-02-19 09:24:06 -07:00
}
}
2018-12-13 06:18:25 -07:00
//_logger.LogDebug("Returning search results {0}", _json.SerializeToString(resultList));
2016-04-24 17:36:10 -07:00
2015-09-30 17:36:46 -07:00
return resultList ;
2014-02-19 09:24:06 -07:00
}
2014-02-21 11:48:15 -07:00
2014-03-02 08:42:21 -07:00
private async Task < IEnumerable < RemoteSearchResult > > GetSearchResults < TLookupType > ( IRemoteSearchProvider < TLookupType > provider , TLookupType searchInfo ,
CancellationToken cancellationToken )
where TLookupType : ItemLookupInfo
{
var results = await provider . GetSearchResults ( searchInfo , cancellationToken ) . ConfigureAwait ( false ) ;
var list = results . ToList ( ) ;
foreach ( var item in list )
{
item . SearchProviderName = provider . Name ;
}
return list ;
}
2014-03-01 15:34:27 -07:00
public Task < HttpResponseInfo > GetSearchImage ( string providerName , string url , CancellationToken cancellationToken )
{
var provider = _metadataProviders . OfType < IRemoteSearchProvider > ( ) . FirstOrDefault ( i = > string . Equals ( i . Name , providerName , StringComparison . OrdinalIgnoreCase ) ) ;
if ( provider = = null )
{
throw new ArgumentException ( "Search provider not found." ) ;
}
return provider . GetImageResponse ( url , cancellationToken ) ;
}
2014-02-21 11:48:15 -07:00
public IEnumerable < IExternalId > GetExternalIds ( IHasProviderIds item )
{
return _externalIds . Where ( i = >
{
try
{
return i . Supports ( item ) ;
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error in {0}.Suports" , i . GetType ( ) . Name ) ;
2014-02-21 11:48:15 -07:00
return false ;
}
} ) ;
}
2016-05-28 23:03:09 -07:00
public IEnumerable < ExternalUrl > GetExternalUrls ( BaseItem item )
2014-02-21 11:48:15 -07:00
{
return GetExternalIds ( item )
. Select ( i = >
{
if ( string . IsNullOrEmpty ( i . UrlFormatString ) )
{
return null ;
}
2014-02-21 14:44:10 -07:00
2014-02-21 11:48:15 -07:00
var value = item . GetProviderId ( i . Key ) ;
if ( string . IsNullOrEmpty ( value ) )
{
return null ;
}
return new ExternalUrl
{
Name = i . Name ,
Url = string . Format ( i . UrlFormatString , value )
} ;
2016-05-28 23:03:09 -07:00
} ) . Where ( i = > i ! = null ) . Concat ( item . GetRelatedUrls ( ) ) ;
2014-02-21 11:48:15 -07:00
}
2014-02-21 14:44:10 -07:00
public IEnumerable < ExternalIdInfo > GetExternalIdInfos ( IHasProviderIds item )
{
return GetExternalIds ( item )
. Select ( i = > new ExternalIdInfo
{
Name = i . Name ,
Key = i . Key ,
UrlFormatString = i . UrlFormatString
} ) ;
}
2015-03-13 21:50:23 -07:00
2017-06-23 09:04:45 -07:00
private Dictionary < Guid , double > _activeRefreshes = new Dictionary < Guid , double > ( ) ;
public Dictionary < Guid , Guid > GetRefreshQueue ( )
{
lock ( _refreshQueueLock )
{
var dict = new Dictionary < Guid , Guid > ( ) ;
foreach ( var item in _refreshQueue )
{
dict [ item . Item1 ] = item . Item1 ;
}
return dict ;
}
}
public void OnRefreshStart ( BaseItem item )
{
2019-02-28 15:22:57 -07:00
//_logger.LogInformation("OnRefreshStart {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
2017-06-23 09:04:45 -07:00
var id = item . Id ;
lock ( _activeRefreshes )
{
_activeRefreshes [ id ] = 0 ;
}
2019-03-13 14:32:52 -07:00
RefreshStarted ? . Invoke ( this , new GenericEventArgs < BaseItem > ( item ) ) ;
2017-06-23 09:04:45 -07:00
}
public void OnRefreshComplete ( BaseItem item )
{
2019-02-28 15:22:57 -07:00
//_logger.LogInformation("OnRefreshComplete {0}", item.Id.ToString("N", CultureInfo.InvariantCulture));
2017-06-23 09:04:45 -07:00
lock ( _activeRefreshes )
{
_activeRefreshes . Remove ( item . Id ) ;
}
2019-03-13 14:32:52 -07:00
RefreshCompleted ? . Invoke ( this , new GenericEventArgs < BaseItem > ( item ) ) ;
2017-06-23 09:04:45 -07:00
}
public double? GetRefreshProgress ( Guid id )
{
lock ( _activeRefreshes )
{
2019-01-17 12:24:39 -07:00
if ( _activeRefreshes . TryGetValue ( id , out double value ) )
2017-06-23 09:04:45 -07:00
{
return value ;
}
return null ;
}
}
public void OnRefreshProgress ( BaseItem item , double progress )
{
2019-02-28 15:22:57 -07:00
//_logger.LogInformation("OnRefreshProgress {0} {1}", item.Id.ToString("N", CultureInfo.InvariantCulture), progress);
2017-06-23 09:04:45 -07:00
var id = item . Id ;
lock ( _activeRefreshes )
{
if ( _activeRefreshes . ContainsKey ( id ) )
{
_activeRefreshes [ id ] = progress ;
2019-03-13 14:32:52 -07:00
RefreshProgress ? . Invoke ( this , new GenericEventArgs < Tuple < BaseItem , double > > ( new Tuple < BaseItem , double > ( item , progress ) ) ) ;
2017-06-23 09:04:45 -07:00
}
else
{
2017-10-28 20:52:05 -07:00
// TODO: Need to hunt down the conditions for this happening
2019-02-28 15:22:57 -07:00
//throw new Exception(string.Format("Refresh for item {0} {1} is not in progress", item.GetType().Name, item.Id.ToString("N", CultureInfo.InvariantCulture)));
2017-06-23 09:04:45 -07:00
}
}
}
2017-04-29 19:37:51 -07:00
private readonly SimplePriorityQueue < Tuple < Guid , MetadataRefreshOptions > > _refreshQueue =
new SimplePriorityQueue < Tuple < Guid , MetadataRefreshOptions > > ( ) ;
2015-03-13 21:50:23 -07:00
2016-10-27 00:58:33 -07:00
private readonly object _refreshQueueLock = new object ( ) ;
private bool _isProcessingRefreshQueue ;
2015-03-13 21:50:23 -07:00
2017-04-29 19:37:51 -07:00
public void QueueRefresh ( Guid id , MetadataRefreshOptions options , RefreshPriority priority )
2015-03-13 21:50:23 -07:00
{
if ( _disposed )
{
return ;
}
2017-04-29 19:37:51 -07:00
_refreshQueue . Enqueue ( new Tuple < Guid , MetadataRefreshOptions > ( id , options ) , ( int ) priority ) ;
2015-03-13 21:50:23 -07:00
2016-10-27 00:58:33 -07:00
lock ( _refreshQueueLock )
2015-03-13 21:50:23 -07:00
{
2016-10-27 00:58:33 -07:00
if ( ! _isProcessingRefreshQueue )
2015-03-13 21:50:23 -07:00
{
2016-10-27 00:58:33 -07:00
_isProcessingRefreshQueue = true ;
2017-05-27 00:19:09 -07:00
Task . Run ( StartProcessingRefreshQueue ) ;
2015-03-13 21:50:23 -07:00
}
}
}
2016-10-27 00:58:33 -07:00
private async Task StartProcessingRefreshQueue ( )
2015-03-13 21:50:23 -07:00
{
var libraryManager = _libraryManagerFactory ( ) ;
2017-06-05 23:13:49 -07:00
if ( _disposed )
{
return ;
}
var cancellationToken = _disposeCancellationTokenSource . Token ;
2019-01-17 12:24:39 -07:00
while ( _refreshQueue . TryDequeue ( out Tuple < Guid , MetadataRefreshOptions > refreshItem ) )
2015-03-13 21:50:23 -07:00
{
if ( _disposed )
{
return ;
}
2015-10-04 11:10:50 -07:00
try
2015-03-13 21:50:23 -07:00
{
2015-10-04 11:10:50 -07:00
var item = libraryManager . GetItemById ( refreshItem . Item1 ) ;
if ( item ! = null )
2015-03-14 08:38:16 -07:00
{
2015-07-17 19:52:27 -07:00
// Try to throttle this a little bit.
await Task . Delay ( 100 ) . ConfigureAwait ( false ) ;
2015-03-14 08:38:16 -07:00
var artist = item as MusicArtist ;
var task = artist = = null
2017-06-05 23:13:49 -07:00
? RefreshItem ( item , refreshItem . Item2 , cancellationToken )
: RefreshArtist ( artist , refreshItem . Item2 , cancellationToken ) ;
2015-03-14 08:38:16 -07:00
await task . ConfigureAwait ( false ) ;
}
2015-10-04 11:10:50 -07:00
}
2017-06-05 23:13:49 -07:00
catch ( OperationCanceledException )
{
break ;
}
2015-10-04 11:10:50 -07:00
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error refreshing item" ) ;
2015-03-13 21:50:23 -07:00
}
2015-03-14 08:38:16 -07:00
}
2016-10-27 00:58:33 -07:00
lock ( _refreshQueueLock )
{
_isProcessingRefreshQueue = false ;
}
2015-03-14 08:38:16 -07:00
}
private async Task RefreshItem ( BaseItem item , MetadataRefreshOptions options , CancellationToken cancellationToken )
{
2017-06-05 23:13:49 -07:00
await item . RefreshMetadata ( options , cancellationToken ) . ConfigureAwait ( false ) ;
2015-03-14 08:38:16 -07:00
2017-01-05 21:38:03 -07:00
// Collection folders don't validate their children so we'll have to simulate that here
2019-03-13 14:32:52 -07:00
if ( item is CollectionFolder collectionFolder )
2017-01-05 21:38:03 -07:00
{
2017-06-05 23:13:49 -07:00
await RefreshCollectionFolderChildren ( options , collectionFolder , cancellationToken ) . ConfigureAwait ( false ) ;
2017-01-05 21:38:03 -07:00
}
else
2015-03-14 08:38:16 -07:00
{
2019-03-13 14:32:52 -07:00
if ( item is Folder folder )
2015-03-14 08:38:16 -07:00
{
2017-06-23 09:04:45 -07:00
await folder . ValidateChildren ( new SimpleProgress < double > ( ) , cancellationToken , options ) . ConfigureAwait ( false ) ;
2015-03-13 21:50:23 -07:00
}
}
2015-03-14 08:38:16 -07:00
}
2015-03-13 21:50:23 -07:00
2017-06-05 23:13:49 -07:00
private async Task RefreshCollectionFolderChildren ( MetadataRefreshOptions options , CollectionFolder collectionFolder , CancellationToken cancellationToken )
2015-03-14 08:38:16 -07:00
{
2019-03-13 14:32:52 -07:00
foreach ( var child in collectionFolder . GetPhysicalFolders ( ) )
2015-03-14 08:38:16 -07:00
{
2017-06-05 23:13:49 -07:00
await child . RefreshMetadata ( options , cancellationToken ) . ConfigureAwait ( false ) ;
2015-03-14 08:38:16 -07:00
2019-03-13 14:32:52 -07:00
await child . ValidateChildren ( new SimpleProgress < double > ( ) , cancellationToken , options , true ) . ConfigureAwait ( false ) ;
2015-03-14 08:38:16 -07:00
}
}
2017-06-05 23:13:49 -07:00
private async Task RefreshArtist ( MusicArtist item , MetadataRefreshOptions options , CancellationToken cancellationToken )
2015-03-14 08:38:16 -07:00
{
2017-01-21 13:27:07 -07:00
var albums = _libraryManagerFactory ( )
. GetItemList ( new InternalItemsQuery
{
2019-03-13 14:32:52 -07:00
IncludeItemTypes = new [ ] { nameof ( MusicAlbum ) } ,
2018-09-12 10:26:21 -07:00
ArtistIds = new [ ] { item . Id } ,
2017-05-21 00:25:49 -07:00
DtoOptions = new DtoOptions ( false )
{
EnableImages = false
}
2017-01-21 13:27:07 -07:00
} )
2019-03-13 14:32:52 -07:00
. OfType < MusicAlbum > ( ) ;
2015-03-14 08:38:16 -07:00
var musicArtists = albums
2016-03-13 18:34:24 -07:00
. Select ( i = > i . MusicArtist )
2019-03-13 14:32:52 -07:00
. Where ( i = > i ! = null ) ;
2015-03-14 08:38:16 -07:00
2017-06-23 09:04:45 -07:00
var musicArtistRefreshTasks = musicArtists . Select ( i = > i . ValidateChildren ( new SimpleProgress < double > ( ) , cancellationToken , options , true ) ) ;
2015-03-14 08:38:16 -07:00
await Task . WhenAll ( musicArtistRefreshTasks ) . ConfigureAwait ( false ) ;
try
{
2017-06-05 23:13:49 -07:00
await item . RefreshMetadata ( options , cancellationToken ) . ConfigureAwait ( false ) ;
2015-03-14 08:38:16 -07:00
}
catch ( Exception ex )
{
2018-12-20 05:11:26 -07:00
_logger . LogError ( ex , "Error refreshing library" ) ;
2015-03-14 08:38:16 -07:00
}
}
2018-09-12 10:26:21 -07:00
public Task RefreshFullItem ( BaseItem item , MetadataRefreshOptions options ,
2015-03-14 08:38:16 -07:00
CancellationToken cancellationToken )
{
2018-09-12 10:26:21 -07:00
return RefreshItem ( item , options , cancellationToken ) ;
2015-03-13 21:50:23 -07:00
}
private bool _disposed ;
public void Dispose ( )
{
_disposed = true ;
2017-06-05 23:13:49 -07:00
if ( ! _disposeCancellationTokenSource . IsCancellationRequested )
{
_disposeCancellationTokenSource . Cancel ( ) ;
}
2015-03-13 21:50:23 -07:00
}
2013-02-20 18:33:05 -07:00
}
2018-12-28 08:48:26 -07:00
}