From 69fed2c3765cbc96211a2d21263f8fe4882767a7 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 20 Jan 2014 13:46:08 -0500 Subject: [PATCH] #680 - file organization --- .../Library/FileOrganizationService.cs | 47 +++++++++++++++++++ MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + .../IFileOrganizationService.cs | 4 +- .../IFileOrganizationRepository.cs | 4 +- .../TV/SeriesPostScanTask.cs | 17 ++++--- .../FileOrganizationService.cs | 4 +- .../OrganizerScheduledTask.cs | 7 ++- .../FileOrganization/TvFileSorter.cs | 34 ++++++++++++-- .../SqliteFileOrganizationRepository.cs | 6 +-- 9 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 MediaBrowser.Api/Library/FileOrganizationService.cs diff --git a/MediaBrowser.Api/Library/FileOrganizationService.cs b/MediaBrowser.Api/Library/FileOrganizationService.cs new file mode 100644 index 0000000000..529a755061 --- /dev/null +++ b/MediaBrowser.Api/Library/FileOrganizationService.cs @@ -0,0 +1,47 @@ +using MediaBrowser.Controller.FileOrganization; +using MediaBrowser.Model.FileOrganization; +using MediaBrowser.Model.Querying; +using ServiceStack; + +namespace MediaBrowser.Api.Library +{ + [Route("/Library/FileOrganization/Results", "GET")] + [Api(Description = "Gets file organization results")] + public class GetFileOrganizationActivity : IReturn> + { + /// + /// Skips over a given number of items within the results. Use for paging. + /// + /// The start index. + [ApiMember(Name = "StartIndex", Description = "Optional. The record index to start at. All items with a lower index will be dropped from the results.", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? StartIndex { get; set; } + + /// + /// The maximum number of items to return + /// + /// The limit. + [ApiMember(Name = "Limit", Description = "Optional. The maximum number of records to return", IsRequired = false, DataType = "int", ParameterType = "query", Verb = "GET")] + public int? Limit { get; set; } + } + + public class FileOrganizationService : BaseApiService + { + private readonly IFileOrganizationService _iFileOrganizationService; + + public FileOrganizationService(IFileOrganizationService iFileOrganizationService) + { + _iFileOrganizationService = iFileOrganizationService; + } + + public object Get(GetFileOrganizationActivity request) + { + var result = _iFileOrganizationService.GetResults(new FileOrganizationResultQuery + { + Limit = request.Limit, + StartIndex = request.Limit + }); + + return ToOptimizedResult(result); + } + } +} diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index a46f82ae05..f9dfadd1f9 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -87,6 +87,7 @@ + diff --git a/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs b/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs index 993c04c281..58a6125087 100644 --- a/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs +++ b/MediaBrowser.Controller/FileOrganization/IFileOrganizationService.cs @@ -1,5 +1,5 @@ using MediaBrowser.Model.FileOrganization; -using System.Collections.Generic; +using MediaBrowser.Model.Querying; using System.Threading; using System.Threading.Tasks; @@ -25,6 +25,6 @@ namespace MediaBrowser.Controller.FileOrganization /// /// The query. /// IEnumerable{FileOrganizationResult}. - IEnumerable GetResults(FileOrganizationResultQuery query); + QueryResult GetResults(FileOrganizationResultQuery query); } } diff --git a/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs b/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs index 9f5cc1579a..47cfcb56eb 100644 --- a/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs +++ b/MediaBrowser.Controller/Persistence/IFileOrganizationRepository.cs @@ -1,5 +1,5 @@ using MediaBrowser.Model.FileOrganization; -using System.Collections.Generic; +using MediaBrowser.Model.Querying; using System.Threading; using System.Threading.Tasks; @@ -20,6 +20,6 @@ namespace MediaBrowser.Controller.Persistence /// /// The query. /// IEnumerable{FileOrganizationResult}. - IEnumerable GetResults(FileOrganizationResultQuery query); + QueryResult GetResults(FileOrganizationResultQuery query); } } diff --git a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs index cc24cad5df..adc61fb736 100644 --- a/MediaBrowser.Providers/TV/SeriesPostScanTask.cs +++ b/MediaBrowser.Providers/TV/SeriesPostScanTask.cs @@ -59,7 +59,7 @@ namespace MediaBrowser.Providers.TV await new MissingEpisodeProvider(_logger, _config).Run(seriesGroups, cancellationToken).ConfigureAwait(false); var numComplete = 0; - + foreach (var series in seriesList) { cancellationToken.ThrowIfCancellationRequested(); @@ -68,9 +68,7 @@ namespace MediaBrowser.Providers.TV .OfType() .ToList(); - series.SpecialFeatureIds = episodes - .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) - .Select(i => i.Id) + var physicalEpisodes = episodes.Where(i => i.LocationType != LocationType.Virtual) .ToList(); series.SeasonCount = episodes @@ -79,7 +77,12 @@ namespace MediaBrowser.Providers.TV .Distinct() .Count(); - series.DateLastEpisodeAdded = episodes.Select(i => i.DateCreated) + series.SpecialFeatureIds = physicalEpisodes + .Where(i => i.ParentIndexNumber.HasValue && i.ParentIndexNumber.Value == 0) + .Select(i => i.Id) + .ToList(); + + series.DateLastEpisodeAdded = physicalEpisodes.Select(i => i.DateCreated) .OrderByDescending(i => i) .FirstOrDefault(); @@ -166,7 +169,7 @@ namespace MediaBrowser.Providers.TV if (_config.Configuration.EnableInternetProviders) { - hasNewEpisodes = await AddMissingEpisodes(group, seriesDataPath, episodeLookup, cancellationToken) + hasNewEpisodes = await AddMissingEpisodes(group.ToList(), seriesDataPath, episodeLookup, cancellationToken) .ConfigureAwait(false); } @@ -225,7 +228,7 @@ namespace MediaBrowser.Providers.TV /// The episode lookup. /// The cancellation token. /// Task. - private async Task AddMissingEpisodes(IEnumerable series, string seriesDataPath, IEnumerable> episodeLookup, CancellationToken cancellationToken) + private async Task AddMissingEpisodes(List series, string seriesDataPath, IEnumerable> episodeLookup, CancellationToken cancellationToken) { var existingEpisodes = series.SelectMany(s => s.RecursiveChildren.OfType()).ToList(); diff --git a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs index 9d8236bde1..bcbff74885 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/FileOrganizationService.cs @@ -2,7 +2,7 @@ using MediaBrowser.Controller.FileOrganization; using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.FileOrganization; -using System.Collections.Generic; +using MediaBrowser.Model.Querying; using System.Threading; using System.Threading.Tasks; @@ -30,7 +30,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization return _repo.SaveResult(result, cancellationToken); } - public IEnumerable GetResults(FileOrganizationResultQuery query) + public QueryResult GetResults(FileOrganizationResultQuery query) { return _repo.GetResults(query); } diff --git a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs index 5c5a83cb6d..35a3ba0873 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/OrganizerScheduledTask.cs @@ -2,6 +2,7 @@ using MediaBrowser.Common.ScheduledTasks; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.FileOrganization; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Logging; using System; @@ -18,14 +19,16 @@ namespace MediaBrowser.Server.Implementations.FileOrganization private readonly ILibraryManager _libraryManager; private readonly IFileSystem _fileSystem; private readonly IFileOrganizationService _iFileSortingRepository; + private readonly IDirectoryWatchers _directoryWatchers; - public OrganizerScheduledTask(IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IFileOrganizationService iFileSortingRepository) + public OrganizerScheduledTask(IServerConfigurationManager config, ILogger logger, ILibraryManager libraryManager, IFileSystem fileSystem, IFileOrganizationService iFileSortingRepository, IDirectoryWatchers directoryWatchers) { _config = config; _logger = logger; _libraryManager = libraryManager; _fileSystem = fileSystem; _iFileSortingRepository = iFileSortingRepository; + _directoryWatchers = directoryWatchers; } public string Name @@ -45,7 +48,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization public Task Execute(CancellationToken cancellationToken, IProgress progress) { - return new TvFileSorter(_libraryManager, _logger, _fileSystem, _iFileSortingRepository).Sort(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress); + return new TvFileSorter(_libraryManager, _logger, _fileSystem, _iFileSortingRepository, _directoryWatchers).Sort(_config.Configuration.TvFileOrganizationOptions, cancellationToken, progress); } public IEnumerable GetDefaultTriggers() diff --git a/MediaBrowser.Server.Implementations/FileOrganization/TvFileSorter.cs b/MediaBrowser.Server.Implementations/FileOrganization/TvFileSorter.cs index 24e2c094b8..72f0da207f 100644 --- a/MediaBrowser.Server.Implementations/FileOrganization/TvFileSorter.cs +++ b/MediaBrowser.Server.Implementations/FileOrganization/TvFileSorter.cs @@ -1,6 +1,7 @@ using MediaBrowser.Common.IO; using MediaBrowser.Controller.Entities.TV; using MediaBrowser.Controller.FileOrganization; +using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Providers; using MediaBrowser.Controller.Resolvers; @@ -24,15 +25,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization private readonly ILogger _logger; private readonly IFileSystem _fileSystem; private readonly IFileOrganizationService _iFileSortingRepository; + private readonly IDirectoryWatchers _directoryWatchers; private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public TvFileSorter(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IFileOrganizationService iFileSortingRepository) + public TvFileSorter(ILibraryManager libraryManager, ILogger logger, IFileSystem fileSystem, IFileOrganizationService iFileSortingRepository, IDirectoryWatchers directoryWatchers) { _libraryManager = libraryManager; _logger = logger; _fileSystem = fileSystem; _iFileSortingRepository = iFileSortingRepository; + _directoryWatchers = directoryWatchers; } public async Task Sort(TvFileOrganizationOptions options, CancellationToken cancellationToken, IProgress progress) @@ -48,6 +51,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization progress.Report(10); + var scanLibrary = false; + if (eligibleFiles.Count > 0) { var allSeries = _libraryManager.RootFolder @@ -59,7 +64,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization foreach (var file in eligibleFiles) { - await SortFile(file.FullName, options, allSeries).ConfigureAwait(false); + var result = await SortFile(file.FullName, options, allSeries).ConfigureAwait(false); + + if (result.Status == FileSortingStatus.Success) + { + scanLibrary = true; + } numComplete++; double percent = numComplete; @@ -88,6 +98,12 @@ namespace MediaBrowser.Server.Implementations.FileOrganization } } + if (scanLibrary) + { + await _libraryManager.ValidateMediaLibrary(new Progress(), CancellationToken.None) + .ConfigureAwait(false); + } + progress.Report(100); } @@ -118,7 +134,7 @@ namespace MediaBrowser.Server.Implementations.FileOrganization /// The path. /// The options. /// All series. - private Task SortFile(string path, TvFileOrganizationOptions options, IEnumerable allSeries) + private async Task SortFile(string path, TvFileOrganizationOptions options, IEnumerable allSeries) { _logger.Info("Sorting file {0}", path); @@ -169,7 +185,9 @@ namespace MediaBrowser.Server.Implementations.FileOrganization _logger.Warn(msg); } - return LogResult(result); + await LogResult(result).ConfigureAwait(false); + + return result; } /// @@ -236,6 +254,8 @@ namespace MediaBrowser.Server.Implementations.FileOrganization /// if set to true [copy]. private void PerformFileSorting(TvFileOrganizationOptions options, FileOrganizationResult result, bool copy) { + _directoryWatchers.TemporarilyIgnore(result.TargetPath); + try { if (copy) @@ -250,11 +270,17 @@ namespace MediaBrowser.Server.Implementations.FileOrganization catch (Exception ex) { var errorMsg = string.Format("Failed to move file from {0} to {1}", result.OriginalPath, result.TargetPath); + result.Status = FileSortingStatus.Failure; result.ErrorMessage = errorMsg; _logger.ErrorException(errorMsg, ex); + return; } + finally + { + _directoryWatchers.RemoveTempIgnore(result.TargetPath); + } if (copy) { diff --git a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs b/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs index a95f84f067..e397cc280a 100644 --- a/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs +++ b/MediaBrowser.Server.Implementations/Persistence/SqliteFileOrganizationRepository.cs @@ -2,8 +2,8 @@ using MediaBrowser.Controller.Persistence; using MediaBrowser.Model.FileOrganization; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Querying; using System; -using System.Collections.Generic; using System.Data; using System.IO; using System.Threading; @@ -62,9 +62,9 @@ namespace MediaBrowser.Server.Implementations.Persistence return Task.FromResult(true); } - public IEnumerable GetResults(FileOrganizationResultQuery query) + public QueryResult GetResults(FileOrganizationResultQuery query) { - return new List(); + return new QueryResult(); } ///