diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs index 19f81f152..417eda90c 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs @@ -305,15 +305,18 @@ private void GetBestRelease(LocalAlbumRelease localAlbumRelease, List x.Path))); - var bestDistance = 1.0; - - foreach (var candidateRelease in candidateReleases) + var maxParallelism = Math.Max(1, Environment.ProcessorCount); + var scoredCandidates = candidateReleases + .Select((candidateRelease, index) => new { candidateRelease, index }) + .AsParallel() + .WithDegreeOfParallelism(maxParallelism) + .Select(item => { - var release = candidateRelease.AlbumRelease; - _logger.Debug("Trying Release {0} [{1}, {2} tracks, {3} existing]", release, release.Title, release.TrackCount, candidateRelease.ExistingTracks.Count); + var release = item.candidateRelease.AlbumRelease; + _logger.Debug("Trying Release {0} [{1}, {2} tracks, {3} existing]", release, release.Title, release.TrackCount, item.candidateRelease.ExistingTracks.Count); var rwatch = System.Diagnostics.Stopwatch.StartNew(); - var extraTrackPaths = candidateRelease.ExistingTracks.Select(x => x.Path).ToList(); + var extraTrackPaths = new HashSet(item.candidateRelease.ExistingTracks.Select(x => x.Path), PathEqualityComparer.Instance); var extraTracks = extraTracksOnDisk.Where(x => extraTrackPaths.Contains(x.Path)).ToList(); var allLocalTracks = localAlbumRelease.LocalTracks.Concat(extraTracks).DistinctBy(x => x.Path).ToList(); @@ -322,25 +325,33 @@ private void GetBestRelease(LocalAlbumRelease localAlbumRelease, List x.currDistance) + .ThenBy(x => x.index) + .First(); + + localAlbumRelease.Distance = best.distance; + localAlbumRelease.AlbumRelease = best.release; + localAlbumRelease.ExistingTracks = best.extraTracks; + localAlbumRelease.TrackMapping = best.mapping; watch.Stop(); _logger.Debug($"Best release: {localAlbumRelease.AlbumRelease} Distance {localAlbumRelease.Distance.NormalizedDistance()} found in {watch.ElapsedMilliseconds}ms"); diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs index aae1a9e6f..5b55626b7 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs @@ -1,7 +1,10 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.IO.Abstractions; using System.Linq; +using System.Threading; +using System.Threading.Tasks; using NLog; using NzbDrone.Common.Extensions; using NzbDrone.Common.Instrumentation.Extensions; @@ -100,19 +103,25 @@ public Tuple, List>> GetLocalTracks( downloadClientItemInfo = Parser.Parser.ParseAlbumTitle(downloadClientItem.Title); } - var i = 1; - foreach (var file in files) + var processedTracks = new ConcurrentBag<(int Index, LocalTrack Track)>(); + var processedDecisions = new ConcurrentBag<(int Index, ImportDecision Decision)>(); + var progress = 0; + var maxParallelism = Math.Max(1, Environment.ProcessorCount); + var filesWithIndex = files.Select((file, index) => new { file, index }).ToList(); + + Parallel.ForEach(filesWithIndex, new ParallelOptions { MaxDegreeOfParallelism = maxParallelism }, item => { - _logger.ProgressInfo($"Reading file {i++}/{files.Count}"); + var current = Interlocked.Increment(ref progress); + _logger.ProgressInfo($"Reading file {current}/{files.Count}"); var localTrack = new LocalTrack { DownloadClientAlbumInfo = downloadClientItemInfo, FolderAlbumInfo = folderInfo, - Path = file.FullName, - Size = file.Length, - Modified = file.LastWriteTimeUtc, - FileTrackInfo = _audioTagService.ReadTags(file.FullName), + Path = item.file.FullName, + Size = item.file.Length, + Modified = item.file.LastWriteTimeUtc, + FileTrackInfo = _audioTagService.ReadTags(item.file.FullName), AdditionalFile = false }; @@ -120,19 +129,22 @@ public Tuple, List>> GetLocalTracks( { // TODO fix otherfiles? _augmentingService.Augment(localTrack, true); - localTracks.Add(localTrack); + processedTracks.Add((item.index, localTrack)); } catch (AugmentingFailedException) { - decisions.Add(new ImportDecision(localTrack, new Rejection("Unable to parse file"))); + processedDecisions.Add((item.index, new ImportDecision(localTrack, new Rejection("Unable to parse file")))); } catch (Exception e) { _logger.Error(e, "Couldn't import file. {0}", localTrack.Path); - decisions.Add(new ImportDecision(localTrack, new Rejection("Unexpected error processing file"))); + processedDecisions.Add((item.index, new ImportDecision(localTrack, new Rejection("Unexpected error processing file")))); } - } + }); + + localTracks.AddRange(processedTracks.OrderBy(x => x.Index).Select(x => x.Track)); + decisions.AddRange(processedDecisions.OrderBy(x => x.Index).Select(x => x.Decision)); _logger.Debug($"Tags parsed for {files.Count} files in {watch.ElapsedMilliseconds}ms");