예제 #1
0
        /// <summary>
        /// Refreshes from provider.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="refreshOptions">The refresh options.</param>
        /// <param name="savedOptions">The saved options.</param>
        /// <param name="backdropLimit">The backdrop limit.</param>
        /// <param name="screenshotLimit">The screenshot limit.</param>
        /// <param name="downloadedImages">The downloaded images.</param>
        /// <param name="result">The result.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task RefreshFromProvider(IHasMetadata item, LibraryOptions libraryOptions,
                                               IRemoteImageProvider provider,
                                               ImageRefreshOptions refreshOptions,
                                               MetadataOptions savedOptions,
                                               int backdropLimit,
                                               int screenshotLimit,
                                               ICollection <ImageType> downloadedImages,
                                               RefreshResult result,
                                               CancellationToken cancellationToken)
        {
            try
            {
                if (!item.SupportsRemoteImageDownloading)
                {
                    return;
                }

                if (!refreshOptions.ReplaceAllImages &&
                    refreshOptions.ReplaceImages.Count == 0 &&
                    ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
                {
                    return;
                }

                _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);

                var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
                {
                    ProviderName             = provider.Name,
                    IncludeAllLanguages      = false,
                    IncludeDisabledProviders = false,
                }, cancellationToken).ConfigureAwait(false);

                var list = images.ToList();
                int minWidth;

                foreach (var imageType in _singularImages)
                {
                    if (!IsEnabled(savedOptions, imageType, item))
                    {
                        continue;
                    }

                    if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
                    {
                        minWidth = savedOptions.GetMinWidth(imageType);
                        var downloaded = await DownloadImage(item, libraryOptions, provider, result, list, minWidth, imageType, cancellationToken).ConfigureAwait(false);

                        if (downloaded)
                        {
                            downloadedImages.Add(imageType);
                        }
                    }
                }

                if (!item.LockedFields.Contains(MetadataFields.Backdrops))
                {
                    minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
                    await DownloadBackdrops(item, libraryOptions, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
                }

                if (!item.LockedFields.Contains(MetadataFields.Screenshots))
                {
                    var hasScreenshots = item as IHasScreenshots;
                    if (hasScreenshots != null)
                    {
                        minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
                        await DownloadBackdrops(item, libraryOptions, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                _logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }
예제 #2
0
        private async Task DownloadBackdrops(IHasMetadata item, LibraryOptions libraryOptions, ImageType imageType, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable <RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
        {
            foreach (var image in images.Where(i => i.Type == imageType))
            {
                if (item.GetImages(imageType).Count() >= limit)
                {
                    break;
                }

                if (image.Width.HasValue && image.Width.Value < minWidth)
                {
                    continue;
                }

                var url = image.Url;

                if (EnableImageStub(item, imageType, libraryOptions))
                {
                    SaveImageStub(item, imageType, new[] { url });
                    result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
                    continue;
                }

                try
                {
                    var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);

                    // If there's already an image of the same size, skip it
                    if (response.ContentLength.HasValue)
                    {
                        try
                        {
                            if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.ContentLength.Value))
                            {
                                response.Content.Dispose();
                                continue;
                            }
                        }
                        catch (IOException ex)
                        {
                            _logger.ErrorException("Error examining images", ex);
                        }
                    }

                    await _providerManager.SaveImage(item, response.Content, response.ContentType, imageType, null, cancellationToken).ConfigureAwait(false);

                    result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
                }
                catch (HttpException ex)
                {
                    // Sometimes providers send back bad url's. Just move onto the next image
                    if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
                    {
                        continue;
                    }
                    break;
                }
            }
        }
예제 #3
0
        protected virtual async Task <RefreshResult> RefreshWithProviders(
            MetadataResult <TItemType> metadata,
            TIdType id,
            MetadataRefreshOptions options,
            ICollection <IMetadataProvider> providers,
            ItemImageProvider imageService,
            CancellationToken cancellationToken)
        {
            var refreshResult = new RefreshResult
            {
                UpdateType = ItemUpdateType.None
            };

            var item = metadata.Item;

            var customProviders = providers.OfType <ICustomMetadataProvider <TItemType> >().ToList();
            var logName         = !item.IsFileProtocol ? item.Name ?? item.Path : item.Path ?? item.Name;

            foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider))
            {
                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            var temp = new MetadataResult <TItemType>
            {
                Item = CreateNew()
            };

            temp.Item.Path = item.Path;

            var userDataList = new List <UserItemData>();

            // If replacing all metadata, run internet providers first
            if (options.ReplaceAllMetadata)
            {
                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType  |= remoteResult.UpdateType;
                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                refreshResult.Failures    += remoteResult.Failures;
            }

            var hasLocalMetadata = false;

            foreach (var provider in providers.OfType <ILocalMetadataProvider <TItemType> >().ToList())
            {
                var providerName = provider.GetType().Name;
                Logger.LogDebug("Running {Provider} for {Item}", providerName, logName);

                var itemInfo = new ItemInfo(item);

                try
                {
                    var localItem = await provider.GetMetadata(itemInfo, options.DirectoryService, cancellationToken).ConfigureAwait(false);

                    if (localItem.HasMetadata)
                    {
                        foreach (var remoteImage in localItem.RemoteImages)
                        {
                            try
                            {
                                await ProviderManager.SaveImage(item, remoteImage.Url, remoteImage.Type, null, cancellationToken).ConfigureAwait(false);

                                refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
                            }
                            catch (HttpRequestException ex)
                            {
                                Logger.LogError(ex, "Could not save {ImageType} image: {Url}", Enum.GetName(remoteImage.Type), remoteImage.Url);
                            }
                        }

                        if (imageService.MergeImages(item, localItem.Images))
                        {
                            refreshResult.UpdateType |= ItemUpdateType.ImageUpdate;
                        }

                        if (localItem.UserDataList != null)
                        {
                            userDataList.AddRange(localItem.UserDataList);
                        }

                        MergeData(localItem, temp, Array.Empty <MetadataField>(), !options.ReplaceAllMetadata, true);
                        refreshResult.UpdateType |= ItemUpdateType.MetadataImport;

                        // Only one local provider allowed per item
                        if (item.IsLocked || localItem.Item.IsLocked || IsFullLocalMetadata(localItem.Item))
                        {
                            hasLocalMetadata = true;
                        }

                        break;
                    }

                    Logger.LogDebug("{Provider} returned no metadata for {Item}", providerName, logName);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    Logger.LogError(ex, "Error in {Provider}", provider.Name);

                    // If a local provider fails, consider that a failure
                    refreshResult.ErrorMessage = ex.Message;
                }
            }

            // Local metadata is king - if any is found don't run remote providers
            if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh || !item.StopRefreshIfLocalMetadataFound))
            {
                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType  |= remoteResult.UpdateType;
                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                refreshResult.Failures    += remoteResult.Failures;
            }

            if (providers.Any(i => i is not ICustomMetadataProvider))
            {
                if (refreshResult.UpdateType > ItemUpdateType.None)
                {
                    if (hasLocalMetadata)
                    {
                        MergeData(temp, metadata, item.LockedFields, true, true);
                    }
                    else
                    {
                        if (!options.RemoveOldMetadata)
                        {
                            MergeData(metadata, temp, Array.Empty <MetadataField>(), false, false);
                        }

                        MergeData(temp, metadata, item.LockedFields, true, false);
                    }
                }
            }

            // var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;

            foreach (var provider in customProviders.Where(i => i is not IPreRefreshProvider))
            {
                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            // ImportUserData(item, userDataList, cancellationToken);

            return(refreshResult);
        }
예제 #4
0
        /// <summary>
        /// Refreshes from provider.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="refreshOptions">The refresh options.</param>
        /// <param name="savedOptions">The saved options.</param>
        /// <param name="downloadedImages">The downloaded images.</param>
        /// <param name="result">The result.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task RefreshFromProvider(IHasMetadata item,
                                               IDynamicImageProvider provider,
                                               ImageRefreshOptions refreshOptions,
                                               MetadataOptions savedOptions,
                                               ICollection <ImageType> downloadedImages,
                                               RefreshResult result,
                                               CancellationToken cancellationToken)
        {
            try
            {
                var images = provider.GetSupportedImages(item);

                foreach (var imageType in images)
                {
                    if (!IsEnabled(savedOptions, imageType, item))
                    {
                        continue;
                    }

                    if (!HasImage(item, imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
                    {
                        _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);

                        var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);

                        if (response.HasImage)
                        {
                            if (!string.IsNullOrEmpty(response.Path))
                            {
                                if (response.Protocol == MediaProtocol.Http)
                                {
                                    _logger.Debug("Setting image url into item {0}", item.Id);
                                    item.SetImage(new ItemImageInfo
                                    {
                                        Path = response.Path,
                                        Type = imageType
                                    }, 0);
                                }
                                else
                                {
                                    var mimeType = MimeTypes.GetMimeType(response.Path);

                                    var stream = _fileSystem.GetFileStream(response.Path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true);

                                    await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                                }
                            }
                            else
                            {
                                var mimeType = "image/" + response.Format.ToString().ToLower();

                                await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                            }

                            downloadedImages.Add(imageType);
                            result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                _logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }
예제 #5
0
        private async Task ExecuteRemoteProviders(TItemType item, TItemType temp, IEnumerable <IRemoteMetadataProvider <TItemType, TIdType> > providers, RefreshResult refreshResult, CancellationToken cancellationToken)
        {
            TIdType id = null;

            var unidentifiedCount = 0;
            var identifiedCount   = 0;

            foreach (var provider in providers)
            {
                var providerName = provider.GetType().Name;
                Logger.Debug("Running {0} for {1}", providerName, item.Path ?? item.Name);

                if (id == null)
                {
                    id = item.GetLookupInfo();
                }
                else
                {
                    MergeNewData(temp, id);
                }

                try
                {
                    var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false);

                    if (result.HasMetadata)
                    {
                        MergeData(result.Item, temp, new List <MetadataFields>(), false, false);

                        refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;

                        identifiedCount++;
                    }
                    else
                    {
                        unidentifiedCount++;
                        Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
                    }
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    unidentifiedCount++;
                    refreshResult.Status       = ProviderRefreshStatus.CompletedWithErrors;
                    refreshResult.ErrorMessage = ex.Message;
                    Logger.ErrorException("Error in {0}", ex, provider.Name);
                }
            }

            var isUnidentified = unidentifiedCount > 0 && identifiedCount == 0;

            if (item.IsUnidentified != isUnidentified)
            {
                item.IsUnidentified      = isUnidentified;
                refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
            }
        }
예제 #6
0
        protected virtual async Task <RefreshResult> RefreshWithProviders(TItemType item, TIdType id, MetadataRefreshOptions options, List <IMetadataProvider> providers, ItemImageProvider imageService, CancellationToken cancellationToken)
        {
            var refreshResult = new RefreshResult
            {
                UpdateType = ItemUpdateType.None,
                Providers  = providers.Select(i => i.GetType().FullName.GetMD5()).ToList()
            };

            var customProviders = providers.OfType <ICustomMetadataProvider <TItemType> >().ToList();

            foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider))
            {
                await RunCustomProvider(provider, item, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            var temp = CreateNew();

            temp.Path = item.Path;
            var successfulProviderCount = 0;
            var failedProviderCount     = 0;

            // If replacing all metadata, run internet providers first
            if (options.ReplaceAllMetadata)
            {
                var remoteResult = await ExecuteRemoteProviders(item, temp, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType   = refreshResult.UpdateType | remoteResult.UpdateType;
                refreshResult.Status       = remoteResult.Status;
                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                successfulProviderCount   += remoteResult.Successes;
                failedProviderCount       += remoteResult.Failures;
            }

            var hasLocalMetadata = false;

            foreach (var provider in providers.OfType <ILocalMetadataProvider <TItemType> >())
            {
                var providerName = provider.GetType().Name;
                Logger.Debug("Running {0} for {1}", providerName, item.Path ?? item.Name);

                var itemInfo = new ItemInfo {
                    Path = item.Path, IsInMixedFolder = item.IsInMixedFolder
                };

                try
                {
                    var localItem = await provider.GetMetadata(itemInfo, cancellationToken).ConfigureAwait(false);

                    if (localItem.HasMetadata)
                    {
                        if (imageService.MergeImages(item, localItem.Images))
                        {
                            refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate;
                        }

                        MergeData(localItem.Item, temp, new List <MetadataFields>(), !options.ReplaceAllMetadata, true);
                        refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;

                        // Only one local provider allowed per item
                        hasLocalMetadata = true;
                        successfulProviderCount++;
                        break;
                    }

                    Logger.Debug("{0} returned no metadata for {1}", providerName, item.Path ?? item.Name);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    failedProviderCount++;

                    Logger.ErrorException("Error in {0}", ex, provider.Name);

                    // If a local provider fails, consider that a failure
                    refreshResult.Status       = ProviderRefreshStatus.Failure;
                    refreshResult.ErrorMessage = ex.Message;

                    if (options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
                    {
                        // If the local provider fails don't continue with remote providers because the user's saved metadata could be lost
                        return(refreshResult);
                    }
                }
            }

            // Local metadata is king - if any is found don't run remote providers
            if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh))
            {
                var remoteResult = await ExecuteRemoteProviders(item, temp, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType = refreshResult.UpdateType | remoteResult.UpdateType;
                if (remoteResult.Status != ProviderRefreshStatus.Success)
                {
                    refreshResult.Status       = remoteResult.Status;
                    refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                }
                successfulProviderCount += remoteResult.Successes;
            }

            if (refreshResult.UpdateType > ItemUpdateType.None)
            {
                MergeData(temp, item, item.LockedFields, true, true);
            }

            var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;

            if (item.IsUnidentified != isUnidentified)
            {
                item.IsUnidentified      = isUnidentified;
                refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;
            }

            foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider)))
            {
                await RunCustomProvider(provider, item, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            return(refreshResult);
        }
예제 #7
0
        private async Task DownloadMultiImages(BaseItem item, ImageType imageType, ImageRefreshOptions refreshOptions, int limit, IRemoteImageProvider provider, RefreshResult result, IEnumerable <RemoteImageInfo> images, int minWidth, CancellationToken cancellationToken)
        {
            foreach (var image in images.Where(i => i.Type == imageType))
            {
                if (item.GetImages(imageType).Count() >= limit)
                {
                    break;
                }

                if (image.Width.HasValue && image.Width.Value < minWidth)
                {
                    continue;
                }

                var url = image.Url;

                if (EnableImageStub(item))
                {
                    SaveImageStub(item, imageType, new[] { url });
                    result.UpdateType |= ItemUpdateType.ImageUpdate;
                    continue;
                }

                try
                {
                    using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);

                    // Sometimes providers send back bad urls. Just move to the next image
                    if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Forbidden)
                    {
                        _logger.LogDebug("{Url} returned {StatusCode}, ignoring", url, response.StatusCode);
                        continue;
                    }

                    if (!response.IsSuccessStatusCode)
                    {
                        _logger.LogWarning("{Url} returned {StatusCode}, skipping all remaining requests", url, response.StatusCode);
                        break;
                    }

                    // If there's already an image of the same file size, skip it unless doing a full refresh
                    if (response.Content.Headers.ContentLength.HasValue && !refreshOptions.IsReplacingImage(imageType))
                    {
                        try
                        {
                            if (item.GetImages(imageType).Any(i => _fileSystem.GetFileInfo(i.Path).Length == response.Content.Headers.ContentLength.Value))
                            {
                                response.Content.Dispose();
                                continue;
                            }
                        }
                        catch (IOException ex)
                        {
                            _logger.LogError(ex, "Error examining images");
                        }
                    }

                    await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);

                    await _providerManager.SaveImage(
                        item,
                        stream,
                        response.Content.Headers.ContentType?.MediaType,
                        imageType,
                        null,
                        cancellationToken).ConfigureAwait(false);

                    result.UpdateType |= ItemUpdateType.ImageUpdate;
                }
                catch (HttpRequestException)
                {
                    break;
                }
            }
        }
예제 #8
0
        /// <summary>
        /// Refreshes from provider.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="refreshOptions">The refresh options.</param>
        /// <param name="savedOptions">The saved options.</param>
        /// <param name="backdropLimit">The backdrop limit.</param>
        /// <param name="screenshotLimit">The screenshot limit.</param>
        /// <param name="result">The result.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task RefreshFromProvider(IHasImages item, IRemoteImageProvider provider, ImageRefreshOptions refreshOptions, MetadataOptions savedOptions, int backdropLimit, int screenshotLimit, RefreshResult result, CancellationToken cancellationToken)
        {
            try
            {
                if (ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit, screenshotLimit))
                {
                    return;
                }

                _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);

                var images = await _providerManager.GetAvailableRemoteImages(item, new RemoteImageQuery
                {
                    ProviderName             = provider.Name,
                    IncludeAllLanguages      = false,
                    IncludeDisabledProviders = false,
                }, cancellationToken).ConfigureAwait(false);

                var list = images.ToList();
                int minWidth;

                foreach (var type in _singularImages)
                {
                    if (savedOptions.IsEnabled(type) && !item.HasImage(type))
                    {
                        minWidth = savedOptions.GetMinWidth(type);
                        await DownloadImage(item, provider, result, list, minWidth, type, cancellationToken).ConfigureAwait(false);
                    }
                }

                minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
                await DownloadBackdrops(item, ImageType.Backdrop, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);

                var hasScreenshots = item as IHasScreenshots;
                if (hasScreenshots != null)
                {
                    minWidth = savedOptions.GetMinWidth(ImageType.Screenshot);
                    await DownloadBackdrops(item, ImageType.Screenshot, screenshotLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                result.Status       = ProviderRefreshStatus.CompletedWithErrors;
                _logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }
예제 #9
0
        private async Task RefreshFromProvider(
            BaseItem item,
            IRemoteImageProvider provider,
            ImageRefreshOptions refreshOptions,
            TypeOptions savedOptions,
            int backdropLimit,
            ICollection <ImageType> downloadedImages,
            RefreshResult result,
            CancellationToken cancellationToken)
        {
            try
            {
                if (!item.SupportsRemoteImageDownloading)
                {
                    return;
                }

                if (!refreshOptions.ReplaceAllImages &&
                    refreshOptions.ReplaceImages.Length == 0 &&
                    ContainsImages(item, provider.GetSupportedImages(item).ToList(), savedOptions, backdropLimit))
                {
                    return;
                }

                _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Name);

                var images = await _providerManager.GetAvailableRemoteImages(
                    item,
                    new RemoteImageQuery(provider.Name)
                {
                    IncludeAllLanguages      = true,
                    IncludeDisabledProviders = false,
                },
                    cancellationToken).ConfigureAwait(false);

                var list = images.ToList();
                int minWidth;

                foreach (var imageType in _singularImages)
                {
                    if (!savedOptions.IsEnabled(imageType))
                    {
                        continue;
                    }

                    if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
                    {
                        minWidth = savedOptions.GetMinWidth(imageType);
                        var downloaded = await DownloadImage(item, provider, result, list, minWidth, imageType, cancellationToken).ConfigureAwait(false);

                        if (downloaded)
                        {
                            downloadedImages.Add(imageType);
                        }
                    }
                }

                minWidth = savedOptions.GetMinWidth(ImageType.Backdrop);
                await DownloadMultiImages(item, ImageType.Backdrop, refreshOptions, backdropLimit, provider, result, list, minWidth, cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                _logger.LogError(ex, "Error in {Provider}", provider.Name);
            }
        }
예제 #10
0
        private async Task <bool> DownloadImage(
            BaseItem item,
            IRemoteImageProvider provider,
            RefreshResult result,
            IEnumerable <RemoteImageInfo> images,
            int minWidth,
            ImageType type,
            CancellationToken cancellationToken)
        {
            var eligibleImages = images
                                 .Where(i => i.Type == type && (i.Width == null || i.Width >= minWidth))
                                 .ToList();

            if (EnableImageStub(item) && eligibleImages.Count > 0)
            {
                SaveImageStub(item, type, eligibleImages.Select(i => i.Url));
                result.UpdateType |= ItemUpdateType.ImageUpdate;
                return(true);
            }

            foreach (var image in eligibleImages)
            {
                var url = image.Url;

                try
                {
                    using var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);

                    // Sometimes providers send back bad urls. Just move to the next image
                    if (response.StatusCode == HttpStatusCode.NotFound || response.StatusCode == HttpStatusCode.Forbidden)
                    {
                        _logger.LogDebug("{Url} returned {StatusCode}, ignoring", url, response.StatusCode);
                        continue;
                    }

                    if (!response.IsSuccessStatusCode)
                    {
                        _logger.LogWarning("{Url} returned {StatusCode}, skipping all remaining requests", url, response.StatusCode);
                        break;
                    }

                    await using var stream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);

                    await _providerManager.SaveImage(
                        item,
                        stream,
                        response.Content.Headers.ContentType?.MediaType,
                        type,
                        null,
                        cancellationToken).ConfigureAwait(false);

                    result.UpdateType |= ItemUpdateType.ImageUpdate;
                    return(true);
                }
                catch (HttpRequestException)
                {
                    break;
                }
            }

            return(false);
        }
예제 #11
0
        private async Task RefreshFromProvider(
            BaseItem item,
            IDynamicImageProvider provider,
            ImageRefreshOptions refreshOptions,
            TypeOptions savedOptions,
            ICollection <ImageType> downloadedImages,
            RefreshResult result,
            CancellationToken cancellationToken)
        {
            try
            {
                var images = provider.GetSupportedImages(item);

                foreach (var imageType in images)
                {
                    if (!savedOptions.IsEnabled(imageType))
                    {
                        continue;
                    }

                    if (!item.HasImage(imageType) || (refreshOptions.IsReplacingImage(imageType) && !downloadedImages.Contains(imageType)))
                    {
                        _logger.LogDebug("Running {Provider} for {Item}", provider.GetType().Name, item.Path ?? item.Name);

                        var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);

                        if (response.HasImage)
                        {
                            if (string.IsNullOrEmpty(response.Path))
                            {
                                var mimeType = response.Format.GetMimeType();

                                await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                            }
                            else
                            {
                                if (response.Protocol == MediaProtocol.Http)
                                {
                                    _logger.LogDebug("Setting image url into item {Item}", item.Id);
                                    var index = item.AllowsMultipleImages(imageType) ? item.GetImages(imageType).Count() : 0;
                                    item.SetImage(
                                        new ItemImageInfo
                                    {
                                        Path = response.Path,
                                        Type = imageType
                                    },
                                        index);
                                }
                                else
                                {
                                    var mimeType = MimeTypes.GetMimeType(response.Path);

                                    var stream = AsyncFile.OpenRead(response.Path);

                                    await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                                }
                            }

                            downloadedImages.Add(imageType);
                            result.UpdateType |= ItemUpdateType.ImageUpdate;
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                _logger.LogError(ex, "Error in {Provider}", provider.Name);
            }
        }
예제 #12
0
        private async Task <RefreshResult> ExecuteRemoteProviders(MetadataResult <TItemType> temp, string logName, TIdType id, IEnumerable <IRemoteMetadataProvider <TItemType, TIdType> > providers, CancellationToken cancellationToken)
        {
            var refreshResult = new RefreshResult();

            var results = new List <MetadataResult <TItemType> >();

            foreach (var provider in providers)
            {
                var providerName = provider.GetType().Name;
                Logger.Debug("Running {0} for {1}", providerName, logName);

                if (id != null)
                {
                    MergeNewData(temp.Item, id);
                }

                try
                {
                    var result = await provider.GetMetadata(id, cancellationToken).ConfigureAwait(false);

                    if (result.HasMetadata)
                    {
                        results.Add(result);

                        refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataDownload;
                    }
                    else
                    {
                        Logger.Debug("{0} returned no metadata for {1}", providerName, logName);
                    }
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    refreshResult.Failures++;
                    refreshResult.ErrorMessage = ex.Message;
                    Logger.ErrorException("Error in {0}", ex, provider.Name);
                }
            }

            var orderedResults    = new List <MetadataResult <TItemType> >();
            var preferredLanguage = NormalizeLanguage(id.MetadataLanguage);

            // prioritize results with matching ResultLanguage
            foreach (var result in results)
            {
                if (!result.QueriedById)
                {
                    break;
                }

                if (string.Equals(NormalizeLanguage(result.ResultLanguage), preferredLanguage, StringComparison.OrdinalIgnoreCase) && result.QueriedById)
                {
                    orderedResults.Add(result);
                }
            }

            // add all other results
            foreach (var result in results)
            {
                if (!orderedResults.Contains(result))
                {
                    orderedResults.Add(result);
                }
            }

            foreach (var result in results)
            {
                MergeData(result, temp, new List <MetadataFields>(), false, false);
            }

            return(refreshResult);
        }
예제 #13
0
        private async Task DownloadImage(IHasImages item, IRemoteImageProvider provider, RefreshResult result, IEnumerable <RemoteImageInfo> images, int minWidth, ImageType type, CancellationToken cancellationToken)
        {
            foreach (var image in images.Where(i => i.Type == type))
            {
                if (image.Width.HasValue && image.Width.Value < minWidth)
                {
                    continue;
                }

                var url = image.Url;

                try
                {
                    var response = await provider.GetImageResponse(url, cancellationToken).ConfigureAwait(false);

                    await _providerManager.SaveImage(item, response.Content, response.ContentType, type, null, cancellationToken).ConfigureAwait(false);

                    result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
                    break;
                }
                catch (HttpException ex)
                {
                    // Sometimes providers send back bad url's. Just move to the next image
                    if (ex.StatusCode.HasValue && ex.StatusCode.Value == HttpStatusCode.NotFound)
                    {
                        continue;
                    }
                    break;
                }
            }
        }
예제 #14
0
        protected virtual async Task <RefreshResult> RefreshWithProviders(MetadataResult <TItemType> metadata,
                                                                          TIdType id,
                                                                          MetadataRefreshOptions options,
                                                                          List <IMetadataProvider> providers,
                                                                          ItemImageProvider imageService,
                                                                          CancellationToken cancellationToken)
        {
            var refreshResult = new RefreshResult
            {
                UpdateType = ItemUpdateType.None,
                Providers  = providers.Select(i => i.GetType().FullName.GetMD5()).ToList()
            };

            var item = metadata.Item;

            var customProviders = providers.OfType <ICustomMetadataProvider <TItemType> >().ToList();
            var logName         = item.LocationType == LocationType.Remote ? item.Name ?? item.Path : item.Path ?? item.Name;

            foreach (var provider in customProviders.Where(i => i is IPreRefreshProvider))
            {
                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            var temp = new MetadataResult <TItemType>
            {
                Item = CreateNew()
            };

            temp.Item.Path = item.Path;

            var userDataList = new List <UserItemData>();

            // If replacing all metadata, run internet providers first
            if (options.ReplaceAllMetadata)
            {
                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType   = refreshResult.UpdateType | remoteResult.UpdateType;
                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                refreshResult.Failures    += remoteResult.Failures;
            }

            var hasLocalMetadata = false;

            foreach (var provider in providers.OfType <ILocalMetadataProvider <TItemType> >().ToList())
            {
                var providerName = provider.GetType().Name;
                Logger.Debug("Running {0} for {1}", providerName, logName);

                var itemInfo = new ItemInfo(item);

                try
                {
                    var localItem = await provider.GetMetadata(itemInfo, options.DirectoryService, cancellationToken).ConfigureAwait(false);

                    if (localItem.HasMetadata)
                    {
                        if (imageService.MergeImages(item, localItem.Images))
                        {
                            refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.ImageUpdate;
                        }

                        if (localItem.UserDataList != null)
                        {
                            userDataList.AddRange(localItem.UserDataList);
                        }

                        MergeData(localItem, temp, new List <MetadataFields>(), !options.ReplaceAllMetadata, true);
                        refreshResult.UpdateType = refreshResult.UpdateType | ItemUpdateType.MetadataImport;

                        // Only one local provider allowed per item
                        if (IsFullLocalMetadata(localItem.Item))
                        {
                            hasLocalMetadata = true;
                        }
                        break;
                    }

                    Logger.Debug("{0} returned no metadata for {1}", providerName, logName);
                }
                catch (OperationCanceledException)
                {
                    throw;
                }
                catch (Exception ex)
                {
                    refreshResult.Failures++;

                    Logger.ErrorException("Error in {0}", ex, provider.Name);

                    // If a local provider fails, consider that a failure
                    refreshResult.ErrorMessage = ex.Message;

                    if (options.MetadataRefreshMode != MetadataRefreshMode.FullRefresh)
                    {
                        // If the local provider fails don't continue with remote providers because the user's saved metadata could be lost
                        return(refreshResult);
                    }
                }
            }

            // Local metadata is king - if any is found don't run remote providers
            if (!options.ReplaceAllMetadata && (!hasLocalMetadata || options.MetadataRefreshMode == MetadataRefreshMode.FullRefresh))
            {
                var remoteResult = await ExecuteRemoteProviders(temp, logName, id, providers.OfType <IRemoteMetadataProvider <TItemType, TIdType> >(), cancellationToken)
                                   .ConfigureAwait(false);

                refreshResult.UpdateType   = refreshResult.UpdateType | remoteResult.UpdateType;
                refreshResult.ErrorMessage = remoteResult.ErrorMessage;
                refreshResult.Failures    += remoteResult.Failures;
            }

            if (providers.Any(i => !(i is ICustomMetadataProvider)))
            {
                if (refreshResult.UpdateType > ItemUpdateType.None)
                {
                    // If no local metadata, take data from item itself
                    if (!hasLocalMetadata)
                    {
                        // TODO: If the new metadata from above has some blank data, this can cause old data to get filled into those empty fields
                        MergeData(metadata, temp, new List <MetadataFields>(), false, true);
                    }

                    MergeData(temp, metadata, item.LockedFields, true, true);
                }
            }

            //var isUnidentified = failedProviderCount > 0 && successfulProviderCount == 0;

            foreach (var provider in customProviders.Where(i => !(i is IPreRefreshProvider)))
            {
                await RunCustomProvider(provider, item, logName, options, refreshResult, cancellationToken).ConfigureAwait(false);
            }

            await ImportUserData(item, userDataList, cancellationToken).ConfigureAwait(false);

            return(refreshResult);
        }
예제 #15
0
        private async Task RunCustomProvider(ICustomMetadataProvider <TItemType> provider, TItemType item, IDirectoryService directoryService, RefreshResult refreshResult, CancellationToken cancellationToken)
        {
            Logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);

            try
            {
                refreshResult.UpdateType = refreshResult.UpdateType | await provider.FetchAsync(item, directoryService, cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                refreshResult.Status       = ProviderRefreshStatus.CompletedWithErrors;
                refreshResult.ErrorMessage = ex.Message;
                Logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }
예제 #16
0
        private async Task RunCustomProvider(ICustomMetadataProvider <TItemType> provider, TItemType item, string logName, MetadataRefreshOptions options, RefreshResult refreshResult, CancellationToken cancellationToken)
        {
            Logger.Debug("Running {0} for {1}", provider.GetType().Name, logName);

            try
            {
                refreshResult.UpdateType = refreshResult.UpdateType | await provider.FetchAsync(item, options, cancellationToken).ConfigureAwait(false);
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                refreshResult.ErrorMessage = ex.Message;
                Logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }
예제 #17
0
        /// <summary>
        /// Refreshes from provider.
        /// </summary>
        /// <param name="item">The item.</param>
        /// <param name="provider">The provider.</param>
        /// <param name="savedOptions">The saved options.</param>
        /// <param name="result">The result.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns>Task.</returns>
        private async Task RefreshFromProvider(IHasImages item, IDynamicImageProvider provider, MetadataOptions savedOptions, RefreshResult result, CancellationToken cancellationToken)
        {
            try
            {
                var images = provider.GetSupportedImages(item);

                foreach (var imageType in images)
                {
                    if (!item.HasImage(imageType) && savedOptions.IsEnabled(imageType))
                    {
                        _logger.Debug("Running {0} for {1}", provider.GetType().Name, item.Path ?? item.Name);

                        var response = await provider.GetImage(item, imageType, cancellationToken).ConfigureAwait(false);

                        if (response.HasImage)
                        {
                            if (!string.IsNullOrEmpty(response.Path))
                            {
                                var mimeType = "image/" + Path.GetExtension(response.Path).TrimStart('.').ToLower();

                                var stream = _fileSystem.GetFileStream(response.Path, FileMode.Open, FileAccess.Read, FileShare.Read, true);

                                await _providerManager.SaveImage(item, stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                            }
                            else
                            {
                                var mimeType = "image/" + response.Format.ToString().ToLower();

                                await _providerManager.SaveImage(item, response.Stream, mimeType, imageType, null, cancellationToken).ConfigureAwait(false);
                            }

                            result.UpdateType = result.UpdateType | ItemUpdateType.ImageUpdate;
                        }
                    }
                }
            }
            catch (OperationCanceledException)
            {
                throw;
            }
            catch (Exception ex)
            {
                result.ErrorMessage = ex.Message;
                result.Status       = ProviderRefreshStatus.CompletedWithErrors;
                _logger.ErrorException("Error in {0}", ex, provider.Name);
            }
        }