/// <inheritdoc/>
        public async Task PerformOrganization(string resultId)
        {
            var result = _repo.GetResult(resultId);

            var options = _config.GetAutoOrganizeOptions();

            if (string.IsNullOrEmpty(result.TargetPath))
            {
                throw new ArgumentException("No target path available.");
            }

            FileOrganizationResult organizeResult;

            switch (result.Type)
            {
            case FileOrganizerType.Episode:
                var episodeOrganizer = new EpisodeFileOrganizer(
                    this,
                    _fileSystem,
                    _loggerFactory.CreateLogger <EpisodeFileOrganizer>(),
                    _libraryManager,
                    _libraryMonitor,
                    _providerManager,
                    _namingOptions);
                organizeResult = await episodeOrganizer.OrganizeEpisodeFile(result.OriginalPath, options.TvOptions, CancellationToken.None)
                                 .ConfigureAwait(false);

                break;

            case FileOrganizerType.Movie:
                var movieOrganizer = new MovieFileOrganizer(
                    this,
                    _fileSystem,
                    _loggerFactory.CreateLogger <MovieFileOrganizer>(),
                    _libraryManager,
                    _libraryMonitor,
                    _providerManager,
                    _namingOptions);
                organizeResult = await movieOrganizer.OrganizeMovieFile(result.OriginalPath, options.MovieOptions, true, CancellationToken.None)
                                 .ConfigureAwait(false);

                break;

            default:
                throw new OrganizationException("No organizer exist for the type " + result.Type);
            }

            if (organizeResult.Status != FileSortingStatus.Success)
            {
                throw new OrganizationException(result.StatusMessage);
            }
        }
        /// <summary>
        /// Perform organization for the TV watch folders.
        /// </summary>
        /// <param name="options">The organization options.</param>
        /// <param name="progress">The <see cref="IProgress{T}"/> to use for reporting operation progress.</param>
        /// <param name="cancellationToken">A cancellation token for the operation.</param>
        /// <returns>A task representing the operation completion.</returns>
        public async Task Organize(
            TvFileOrganizationOptions options,
            IProgress <double> progress,
            CancellationToken cancellationToken)
        {
            var libraryFolderPaths = _libraryManager.GetVirtualFolders().SelectMany(i => i.Locations).ToList();

            var watchLocations = options.WatchLocations
                                 .Where(i => IsValidWatchLocation(i, libraryFolderPaths))
                                 .ToList();

            var eligibleFiles = watchLocations.SelectMany(GetFilesToOrganize)
                                .OrderBy(_fileSystem.GetCreationTimeUtc)
                                .Where(i => EnableOrganization(i, options))
                                .ToList();

            var processedFolders = new HashSet <string>();

            progress.Report(10);

            if (eligibleFiles.Count > 0)
            {
                var numComplete = 0;

                var organizer = new EpisodeFileOrganizer(
                    _organizationService,
                    _fileSystem,
                    _loggerFactory.CreateLogger <EpisodeFileOrganizer>(),
                    _libraryManager,
                    _libraryMonitor,
                    _providerManager,
                    _namingOptions);

                foreach (var file in eligibleFiles)
                {
                    cancellationToken.ThrowIfCancellationRequested();

                    try
                    {
                        var result = await organizer.OrganizeEpisodeFile(file.FullName, options, cancellationToken).ConfigureAwait(false);

                        var directoryName = Path.GetDirectoryName(file.FullName);
                        if (result.Status == FileSortingStatus.Success && !processedFolders.Contains(directoryName, StringComparer.OrdinalIgnoreCase))
                        {
                            processedFolders.Add(directoryName);
                        }
                    }
                    catch (OperationCanceledException)
                    {
                        break;
                    }
                    catch (Exception ex)
                    {
                        _logger.LogError(ex, "Error organizing episode {0}", file.FullName);
                    }

                    numComplete++;
                    double percent = numComplete;
                    percent /= eligibleFiles.Count;

                    progress.Report(10 + (89 * percent));
                }
            }

            cancellationToken.ThrowIfCancellationRequested();
            progress.Report(99);

            var deleteExtensions = options.LeftOverFileExtensionsToDelete
                                   .Select(i => i.Trim().TrimStart('.'))
                                   .Where(i => !string.IsNullOrEmpty(i))
                                   .Select(i => "." + i)
                                   .ToList();

            // Normal Clean
            Clean(processedFolders, watchLocations, options.DeleteEmptyFolders, deleteExtensions);

            // Extended Clean
            if (options.ExtendedClean)
            {
                Clean(watchLocations, watchLocations, options.DeleteEmptyFolders, deleteExtensions);
            }

            progress.Report(100);
        }