diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index b5afab3e59..180d0292f0 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -331,6 +331,11 @@ namespace MediaBrowser.Model.Configuration public bool DeleteEmptyFolders { get; set; } + /// + /// Will log results but will not actually make any changes + /// + public bool EnableTrialMode { get; set; } + public FileSortingOptions() { MinFileSizeMb = 50; @@ -344,6 +349,8 @@ namespace MediaBrowser.Model.Configuration SeasonFolderPattern = "Season %s"; SeasonZeroFolderName = "Season 0"; + + EnableTrialMode = true; } } } diff --git a/MediaBrowser.Server.Implementations/FileSorting/SortingScheduledTask.cs b/MediaBrowser.Server.Implementations/FileSorting/SortingScheduledTask.cs index 201e282c03..a9d7ada94f 100644 --- a/MediaBrowser.Server.Implementations/FileSorting/SortingScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/FileSorting/SortingScheduledTask.cs @@ -1,10 +1,10 @@ -using MediaBrowser.Common.ScheduledTasks; +using MediaBrowser.Common.IO; +using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -15,12 +15,14 @@ namespace MediaBrowser.Server.Implementations.FileSorting private readonly IServerConfigurationManager _config; private readonly ILogger _logger; private readonly ILibraryManager _libraryManager; + private readonly IFileSystem _fileSystem; - public SortingScheduledTask(IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager) + public SortingScheduledTask(IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem) { _config = config; _logger = logger; _libraryManager = libraryManager; + _fileSystem = fileSystem; } public string Name @@ -45,34 +47,7 @@ namespace MediaBrowser.Server.Implementations.FileSorting private void SortFiles(CancellationToken cancellationToken, IProgress progress) { - var numComplete = 0; - - var paths = _config.Configuration.FileSortingOptions.TvWatchLocations.ToList(); - - foreach (var path in paths) - { - cancellationToken.ThrowIfCancellationRequested(); - - try - { - SortFiles(path); - } - catch (Exception ex) - { - _logger.ErrorException("Error sorting files from {0}", ex, path); - } - - numComplete++; - double percent = numComplete; - percent /= paths.Count; - - progress.Report(100 * percent); - } - } - - private void SortFiles(string path) - { - new TvFileSorter(_libraryManager, _logger).Sort(path, _config.Configuration.FileSortingOptions); + new TvFileSorter(_libraryManager, _logger, _fileSystem).Sort(_config.Configuration.FileSortingOptions, cancellationToken, progress); } public IEnumerable GetDefaultTriggers() diff --git a/MediaBrowser.Server.Implementations/FileSorting/TvFileSorter.cs b/MediaBrowser.Server.Implementations/FileSorting/TvFileSorter.cs index fad4c6ce2f..cac05d426e 100644 --- a/MediaBrowser.Server.Implementations/FileSorting/TvFileSorter.cs +++ b/MediaBrowser.Server.Implementations/FileSorting/TvFileSorter.cs @@ -1,5 +1,4 @@ -using System.Globalization; -using MediaBrowser.Common.IO; +using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; @@ -9,8 +8,10 @@ using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; +using System.Threading; namespace MediaBrowser.Server.Implementations.FileSorting { @@ -20,23 +21,25 @@ namespace MediaBrowser.Server.Implementations.FileSorting private readonly ILogger _logger; private readonly IFileSystem _fileSystem; - private static CultureInfo _usCulture = new CultureInfo("en-US"); + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public TvFileSorter(ILibraryManager libraryManager, ILogger logger) + public TvFileSorter(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem) { _libraryManager = libraryManager; _logger = logger; + _fileSystem = fileSystem; } - public void Sort(string path, FileSortingOptions options) + public void Sort(FileSortingOptions options, CancellationToken cancellationToken, IProgress progress) { var minFileBytes = options.MinFileSizeMb * 1024 * 1024; - var eligibleFiles = new DirectoryInfo(path) - .EnumerateFiles("*", SearchOption.AllDirectories) + var eligibleFiles = options.TvWatchLocations.SelectMany(GetEligibleFiles) .Where(i => EntityResolutionHelper.IsVideoFile(i.FullName) && i.Length >= minFileBytes) .ToList(); + progress.Report(10); + if (eligibleFiles.Count > 0) { var allSeries = _libraryManager.RootFolder @@ -44,20 +47,55 @@ namespace MediaBrowser.Server.Implementations.FileSorting .Where(i => i.LocationType == LocationType.FileSystem) .ToList(); + var numComplete = 0; + foreach (var file in eligibleFiles) { SortFile(file.FullName, options, allSeries); + + numComplete++; + double percent = numComplete; + percent /= eligibleFiles.Count; + + progress.Report(100 * percent); } } - if (options.LeftOverFileExtensionsToDelete.Length > 0) + cancellationToken.ThrowIfCancellationRequested(); + progress.Report(99); + + if (!options.EnableTrialMode) { - DeleteLeftOverFiles(path, options.LeftOverFileExtensionsToDelete); + foreach (var path in options.TvWatchLocations) + { + if (options.LeftOverFileExtensionsToDelete.Length > 0) + { + DeleteLeftOverFiles(path, options.LeftOverFileExtensionsToDelete); + } + + if (options.DeleteEmptyFolders) + { + DeleteEmptyFolders(path); + } + } } - if (options.DeleteEmptyFolders) + progress.Report(100); + } + + private IEnumerable GetEligibleFiles(string path) + { + try { - DeleteEmptyFolders(path); + return new DirectoryInfo(path) + .EnumerateFiles("*", SearchOption.AllDirectories) + .ToList(); + } + catch (IOException ex) + { + _logger.ErrorException("Error getting files from {0}", ex, path); + + return new List(); } } @@ -147,8 +185,8 @@ namespace MediaBrowser.Server.Implementations.FileSorting var episodeFileName = string.Format("{0} - {1}x{2} - {3}", _fileSystem.GetValidFilename(series.Name), - seasonNumber.ToString(_usCulture), - episodeNumber.ToString("00", _usCulture), + seasonNumber.ToString(UsCulture), + episodeNumber.ToString("00", UsCulture), _fileSystem.GetValidFilename(episode.Name) ); @@ -158,6 +196,13 @@ namespace MediaBrowser.Server.Implementations.FileSorting return newPath; } + /// + /// Gets the season folder path. + /// + /// The series. + /// The season number. + /// The options. + /// System.String. private string GetSeasonFolderPath(Series series, int seasonNumber, FileSortingOptions options) { // If there's already a season folder, use that @@ -184,13 +229,19 @@ namespace MediaBrowser.Server.Implementations.FileSorting } var seasonFolderName = options.SeasonFolderPattern - .Replace("%s", seasonNumber.ToString(_usCulture)) - .Replace("%0s", seasonNumber.ToString("00", _usCulture)) - .Replace("%00s", seasonNumber.ToString("000", _usCulture)); + .Replace("%s", seasonNumber.ToString(UsCulture)) + .Replace("%0s", seasonNumber.ToString("00", UsCulture)) + .Replace("%00s", seasonNumber.ToString("000", UsCulture)); return Path.Combine(path, _fileSystem.GetValidFilename(seasonFolderName)); } + /// + /// Gets the matching series. + /// + /// Name of the series. + /// All series. + /// Series. private Series GetMatchingSeries(string seriesName, IEnumerable allSeries) { int? yearInName;