using MediaBrowser.Common.Progress; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using MoreLinq; using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; namespace MediaBrowser.Server.Implementations.Library.Validators { /// /// Class PeopleValidator /// public class PeopleValidator { /// /// The _library manager /// private readonly ILibraryManager _libraryManager; /// /// The _logger /// private readonly ILogger _logger; private readonly IEnumerable _prescanTasks; /// /// Initializes a new instance of the class. /// /// The library manager. /// The prescan tasks. /// The logger. public PeopleValidator(ILibraryManager libraryManager, IEnumerable prescanTasks, ILogger logger) { _libraryManager = libraryManager; _logger = logger; _prescanTasks = prescanTasks; } /// /// Validates the people. /// /// The cancellation token. /// The progress. /// Task. public async Task ValidatePeople(CancellationToken cancellationToken, IProgress progress) { var innerProgress = new ActionableProgress(); innerProgress.RegisterAction(pct => progress.Report(pct * .15)); // Run prescan tasks await RunPrescanTasks(innerProgress, cancellationToken).ConfigureAwait(false); progress.Report(15); var people = _libraryManager.RootFolder.GetRecursiveChildren() .SelectMany(c => c.People) .DistinctBy(p => p.Name, StringComparer.OrdinalIgnoreCase) .ToList(); var numComplete = 0; foreach (var person in people) { cancellationToken.ThrowIfCancellationRequested(); try { var item = _libraryManager.GetPerson(person.Name); await item.RefreshMetadata(cancellationToken).ConfigureAwait(false); } catch (Exception ex) { _logger.ErrorException("Error validating IBN entry {0}", ex, person.Name); } // Update progress numComplete++; double percent = numComplete; percent /= people.Count; progress.Report(15 + 85 * percent); } progress.Report(100); _logger.Info("People validation complete"); // Bad practice, i know. But we keep a lot in memory, unfortunately. GC.Collect(2, GCCollectionMode.Forced, true); GC.Collect(2, GCCollectionMode.Forced, true); } /// /// Runs the prescan tasks. /// /// The progress. /// The cancellation token. /// Task. private async Task RunPrescanTasks(IProgress progress, CancellationToken cancellationToken) { var tasks = _prescanTasks.ToList(); var numComplete = 0; var numTasks = tasks.Count; foreach (var task in tasks) { var innerProgress = new ActionableProgress(); // Prevent access to modified closure var currentNumComplete = numComplete; innerProgress.RegisterAction(pct => { double innerPercent = (currentNumComplete * 100) + pct; innerPercent /= numTasks; progress.Report(innerPercent); }); try { await task.Run(innerProgress, cancellationToken); } catch (OperationCanceledException) { _logger.Info("Pre-scan task cancelled: {0}", task.GetType().Name); break; } catch (Exception ex) { _logger.ErrorException("Error running pre-scan task", ex); } numComplete++; double percent = numComplete; percent /= numTasks; progress.Report(percent * 100); } progress.Report(100); } } }