using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Collections; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities.Movies; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.IO; using MediaBrowser.Model.Tasks; using Microsoft.Extensions.Logging; namespace Emby.Server.Implementations.ScheduledTasks.Tasks; /// /// Deletes Path references from collections that no longer exists. /// public class CleanupCollectionPathsTask : IScheduledTask { private readonly ILocalizationManager _localization; private readonly ICollectionManager _collectionManager; private readonly ILogger _logger; private readonly IProviderManager _providerManager; private readonly IFileSystem _fileSystem; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. /// The logger. /// The provider manager. /// The filesystem. public CleanupCollectionPathsTask( ILocalizationManager localization, ICollectionManager collectionManager, ILogger logger, IProviderManager providerManager, IFileSystem fileSystem) { _localization = localization; _collectionManager = collectionManager; _logger = logger; _providerManager = providerManager; _fileSystem = fileSystem; } /// public string Name => _localization.GetLocalizedString("TaskCleanCollections"); /// public string Key => "CleanCollections"; /// public string Description => _localization.GetLocalizedString("TaskCleanCollectionsDescription"); /// public string Category => _localization.GetLocalizedString("TasksMaintenanceCategory"); /// public async Task ExecuteAsync(IProgress progress, CancellationToken cancellationToken) { var collectionsFolder = await _collectionManager.GetCollectionsFolder(false).ConfigureAwait(false); if (collectionsFolder is null) { _logger.LogDebug("There is no collection folder to be found"); return; } var collections = collectionsFolder.Children.OfType().ToArray(); _logger.LogDebug("Found {CollectionLength} Boxsets", collections.Length); var itemsToRemove = new List(); for (var index = 0; index < collections.Length; index++) { var collection = collections[index]; _logger.LogDebug("Check Boxset {CollectionName}", collection.Name); foreach (var collectionLinkedChild in collection.LinkedChildren) { if (!File.Exists(collectionLinkedChild.Path)) { _logger.LogInformation("Item in boxset {CollectionName} cannot be found at {ItemPath}", collection.Name, collectionLinkedChild.Path); itemsToRemove.Add(collectionLinkedChild); } } if (itemsToRemove.Count != 0) { _logger.LogDebug("Update Boxset {CollectionName}", collection.Name); collection.LinkedChildren = collection.LinkedChildren.Except(itemsToRemove).ToArray(); await collection.UpdateToRepositoryAsync(ItemUpdateType.MetadataEdit, cancellationToken) .ConfigureAwait(false); _providerManager.QueueRefresh( collection.Id, new MetadataRefreshOptions(new DirectoryService(_fileSystem)) { ForceSave = true }, RefreshPriority.High); itemsToRemove.Clear(); } progress.Report(100D / collections.Length * (index + 1)); } } /// public IEnumerable GetDefaultTriggers() { return new[] { new TaskTriggerInfo() { Type = TaskTriggerInfo.TriggerStartup } }; } }