diff --git a/src/NzbDrone.Core/MediaFiles/CueSheet.cs b/src/NzbDrone.Core/MediaFiles/CueSheet.cs index 84374635e..3f05ba080 100644 --- a/src/NzbDrone.Core/MediaFiles/CueSheet.cs +++ b/src/NzbDrone.Core/MediaFiles/CueSheet.cs @@ -1,35 +1,10 @@ -using System; using System.Collections.Generic; -using System.IO.Abstractions; -using System.Text; -using System.Text.RegularExpressions; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.MediaFiles { public class CueSheet : ModelBase { - public CueSheet(IFileInfo fileInfo) - { - Path = fileInfo.FullName; - - using (var fs = fileInfo.OpenRead()) - { - var bytes = new byte[fileInfo.Length]; - var encoding = new UTF8Encoding(true); - string content; - while (fs.Read(bytes, 0, bytes.Length) > 0) - { - content = encoding.GetString(bytes); - var lines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); - ParseCueSheet(lines); - - // Single-file cue means it's an unsplit image, which should be specially treated in the pipeline - IsSingleFileRelease = Files.Count == 1; - } - } - } - public class IndexEntry { public int Key { get; set; } @@ -59,143 +34,5 @@ namespace NzbDrone.Core.MediaFiles public string DiscID { get; set; } public string Title { get; set; } public string Performer { get; set; } - private static string _FileKey = "FILE"; - private static string _TrackKey = "TRACK"; - private static string _IndexKey = "INDEX"; - private static string _GenreKey = "REM GENRE"; - private static string _DateKey = "REM DATE"; - private static string _DiscIdKey = "REM DISCID"; - private static string _PerformerKey = "PERFORMER"; - private static string _TitleKey = "TITLE"; - - private string ExtractValue(string line, string keyword) - { - var pattern = keyword + @"\s+(?:(?:\""(.*?)\"")|(.+))"; - var match = Regex.Match(line, pattern); - - if (match.Success) - { - var value = match.Groups[1].Success ? match.Groups[1].Value : match.Groups[2].Value; - return value; - } - - return ""; - } - - private void ParseCueSheet(string[] lines) - { - var i = 0; - try - { - while (true) - { - var line = lines[i]; - if (line.StartsWith(_FileKey)) - { - line = line.Trim(); - line = line.Substring(_FileKey.Length).Trim(); - var filename = line.Split('"')[1]; - var fileDetails = new FileEntry { Name = filename }; - - i++; - line = lines[i]; - while (line.StartsWith(" ")) - { - line = line.Trim(); - if (line.StartsWith(_TrackKey)) - { - line = line.Substring(_TrackKey.Length).Trim(); - } - - var trackDetails = new TrackEntry(); - var trackInfo = line.Split(' '); - if (trackInfo.Length > 0) - { - if (int.TryParse(trackInfo[0], out var number)) - { - trackDetails.Number = number; - } - } - - i++; - line = lines[i]; - while (line.StartsWith(" ")) - { - line = line.Trim(); - if (line.StartsWith(_IndexKey)) - { - line = line.Substring(_IndexKey.Length).Trim(); - var parts = line.Split(' '); - if (parts.Length > 1) - { - if (int.TryParse(parts[0], out var key)) - { - var value = parts[1].Trim('"'); - trackDetails.Indices.Add(new IndexEntry { Key = key, Time = value }); - } - } - - i++; - line = lines[i]; - } - else if (line.StartsWith(_TitleKey)) - { - trackDetails.Title = ExtractValue(line, _TitleKey); - i++; - line = lines[i]; - } - else if (line.StartsWith(_PerformerKey)) - { - trackDetails.Performer = ExtractValue(line, _PerformerKey); - i++; - line = lines[i]; - } - else - { - i++; - line = lines[i]; - } - } - - fileDetails.Tracks.Add(trackDetails); - } - - Files.Add(fileDetails); - } - else if (line.StartsWith(_GenreKey)) - { - Genre = ExtractValue(line, _GenreKey); - i++; - } - else if (line.StartsWith(_DateKey)) - { - Date = ExtractValue(line, _DateKey); - i++; - } - else if (line.StartsWith(_DiscIdKey)) - { - DiscID = ExtractValue(line, _DiscIdKey); - i++; - } - else if (line.StartsWith(_PerformerKey)) - { - Performer = ExtractValue(line, _PerformerKey); - i++; - } - else if (line.StartsWith(_TitleKey)) - { - Title = ExtractValue(line, _TitleKey); - i++; - } - else - { - i++; - } - } - } - catch (IndexOutOfRangeException) - { - } - } } } diff --git a/src/NzbDrone.Core/MediaFiles/CueSheetService.cs b/src/NzbDrone.Core/MediaFiles/CueSheetService.cs new file mode 100644 index 000000000..2783c4435 --- /dev/null +++ b/src/NzbDrone.Core/MediaFiles/CueSheetService.cs @@ -0,0 +1,332 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Abstractions; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using NzbDrone.Common.Extensions; +using NzbDrone.Core.MediaFiles.TrackImport; +using NzbDrone.Core.Music; +using NzbDrone.Core.Parser; +using NzbDrone.Core.Parser.Model; + +namespace NzbDrone.Core.MediaFiles +{ + public class CueSheetInfo + { + public List MusicFiles { get; set; } = new List(); + public IdentificationOverrides IdOverrides { get; set; } + public CueSheet CueSheet { get; set; } + public bool IsForMediaFile(string path) => CueSheet != null && CueSheet.Files.Count > 0 && CueSheet.Files.Any(x => Path.GetFileName(path) == x.Name); + } + + public interface ICueSheetService + { + List> GetImportDecisions(ref List mediaFileList, ImportDecisionMakerInfo itemInfo, ImportDecisionMakerConfig config); + } + + public class CueSheetService : ICueSheetService + { + private readonly IParsingService _parsingService; + private readonly IMakeImportDecision _importDecisionMaker; + + private static string _FileKey = "FILE"; + private static string _TrackKey = "TRACK"; + private static string _IndexKey = "INDEX"; + private static string _GenreKey = "REM GENRE"; + private static string _DateKey = "REM DATE"; + private static string _DiscIdKey = "REM DISCID"; + private static string _PerformerKey = "PERFORMER"; + private static string _TitleKey = "TITLE"; + + public CueSheetService(IParsingService parsingService, + IMakeImportDecision importDecisionMaker) + { + _parsingService = parsingService; + _importDecisionMaker = importDecisionMaker; + } + + public List> GetImportDecisions(ref List mediaFileList, ImportDecisionMakerInfo itemInfo, ImportDecisionMakerConfig config) + { + var decisions = new List>(); + var cueFiles = mediaFileList.Where(x => x.Extension.Equals(".cue")).ToList(); + if (cueFiles.Count == 0) + { + return decisions; + } + + mediaFileList.RemoveAll(l => cueFiles.Contains(l)); + var cueSheetInfos = new List(); + foreach (var cueFile in cueFiles) + { + var cueSheetInfo = GetCueSheetInfo(cueFile, mediaFileList); + cueSheetInfos.Add(cueSheetInfo); + } + + var cueSheetInfosGroupedByDiscId = cueSheetInfos.GroupBy(x => x.CueSheet.DiscID).ToList(); + foreach (var cueSheetInfoGroup in cueSheetInfosGroupedByDiscId) + { + var audioFilesForCues = new List(); + foreach (var cueSheetInfo in cueSheetInfoGroup) + { + audioFilesForCues.AddRange(cueSheetInfo.MusicFiles); + } + + decisions.AddRange(_importDecisionMaker.GetImportDecisions(audioFilesForCues, cueSheetInfoGroup.First().IdOverrides, itemInfo, config, cueSheetInfos)); + + foreach (var cueSheetInfo in cueSheetInfos) + { + if (cueSheetInfo.CueSheet != null) + { + decisions.ForEach(item => + { + if (cueSheetInfo.IsForMediaFile(item.Item.Path)) + { + item.Item.CueSheetPath = cueSheetInfo.CueSheet.Path; + } + }); + } + + mediaFileList.RemoveAll(x => cueSheetInfo.MusicFiles.Contains(x)); + } + } + + var addedTracks = new List(); + decisions.ForEach(decision => + { + if (!decision.Item.IsSingleFileRelease) + { + return; + } + + var cueSheetFindResult = cueSheetInfos.Find(x => x.IsForMediaFile(decision.Item.Path)); + var cueSheet = cueSheetFindResult?.CueSheet; + if (cueSheet == null) + { + return; + } + + if (cueSheet.Files.Count == 0) + { + return; + } + + var tracksFromCueSheet = cueSheet.Files.SelectMany(x => x.Tracks).ToList(); + if (tracksFromCueSheet.Count == 0) + { + return; + } + + var tracksFromRelease = decision.Item.Release.Tracks.Value; + if (tracksFromRelease.Count == 0) + { + return; + } + + decision.Item.Tracks = tracksFromRelease.Where(trackFromRelease => !addedTracks.Contains(trackFromRelease) && tracksFromCueSheet.Any(trackFromCueSheet => string.Equals(trackFromCueSheet.Title, trackFromRelease.Title, StringComparison.InvariantCultureIgnoreCase))).ToList(); + addedTracks.AddRange(decision.Item.Tracks); + }); + + return decisions; + } + + private CueSheet LoadCueSheet(IFileInfo fileInfo) + { + using (var fs = fileInfo.OpenRead()) + { + var bytes = new byte[fileInfo.Length]; + var encoding = new UTF8Encoding(true); + string content; + while (fs.Read(bytes, 0, bytes.Length) > 0) + { + content = encoding.GetString(bytes); + var lines = content.Split(new string[] { Environment.NewLine }, StringSplitOptions.None); + var cueSheet = ParseLines(lines); + + // Single-file cue means it's an unsplit image, which should be specially treated in the pipeline + cueSheet.IsSingleFileRelease = cueSheet.Files.Count == 1; + cueSheet.Path = fileInfo.FullName; + + return cueSheet; + } + } + + return new CueSheet(); + } + + private string ExtractValue(string line, string keyword) + { + var pattern = keyword + @"\s+(?:(?:\""(.*?)\"")|(.+))"; + var match = Regex.Match(line, pattern); + + if (match.Success) + { + var value = match.Groups[1].Success ? match.Groups[1].Value : match.Groups[2].Value; + return value; + } + + return ""; + } + + private CueSheet ParseLines(string[] lines) + { + var cueSheet = new CueSheet(); + + var i = 0; + try + { + while (true) + { + var line = lines[i]; + if (line.StartsWith(_FileKey)) + { + line = line.Trim(); + line = line.Substring(_FileKey.Length).Trim(); + var filename = line.Split('"')[1]; + var fileDetails = new CueSheet.FileEntry { Name = filename }; + + i++; + line = lines[i]; + while (line.StartsWith(" ")) + { + line = line.Trim(); + if (line.StartsWith(_TrackKey)) + { + line = line.Substring(_TrackKey.Length).Trim(); + } + + var trackDetails = new CueSheet.TrackEntry(); + var trackInfo = line.Split(' '); + if (trackInfo.Length > 0) + { + if (int.TryParse(trackInfo[0], out var number)) + { + trackDetails.Number = number; + } + } + + i++; + line = lines[i]; + while (line.StartsWith(" ")) + { + line = line.Trim(); + if (line.StartsWith(_IndexKey)) + { + line = line.Substring(_IndexKey.Length).Trim(); + var parts = line.Split(' '); + if (parts.Length > 1) + { + if (int.TryParse(parts[0], out var key)) + { + var value = parts[1].Trim('"'); + trackDetails.Indices.Add(new CueSheet.IndexEntry { Key = key, Time = value }); + } + } + + i++; + line = lines[i]; + } + else if (line.StartsWith(_TitleKey)) + { + trackDetails.Title = ExtractValue(line, _TitleKey); + i++; + line = lines[i]; + } + else if (line.StartsWith(_PerformerKey)) + { + trackDetails.Performer = ExtractValue(line, _PerformerKey); + i++; + line = lines[i]; + } + else + { + i++; + line = lines[i]; + } + } + + fileDetails.Tracks.Add(trackDetails); + } + + cueSheet.Files.Add(fileDetails); + } + else if (line.StartsWith(_GenreKey)) + { + cueSheet.Genre = ExtractValue(line, _GenreKey); + i++; + } + else if (line.StartsWith(_DateKey)) + { + cueSheet.Date = ExtractValue(line, _DateKey); + i++; + } + else if (line.StartsWith(_DiscIdKey)) + { + cueSheet.DiscID = ExtractValue(line, _DiscIdKey); + i++; + } + else if (line.StartsWith(_PerformerKey)) + { + cueSheet.Performer = ExtractValue(line, _PerformerKey); + i++; + } + else if (line.StartsWith(_TitleKey)) + { + cueSheet.Title = ExtractValue(line, _TitleKey); + i++; + } + else + { + i++; + } + } + } + catch (IndexOutOfRangeException) + { + } + + return cueSheet; + } + + private CueSheetInfo GetCueSheetInfo(IFileInfo cueFile, List musicFiles) + { + var cueSheetInfo = new CueSheetInfo(); + var cueSheet = LoadCueSheet(cueFile); + if (cueSheet == null) + { + return cueSheetInfo; + } + + cueSheetInfo.CueSheet = cueSheet; + cueSheetInfo.IdOverrides = new IdentificationOverrides(); + + Artist artistFromCue = null; + if (!cueSheet.Performer.Empty()) + { + artistFromCue = _parsingService.GetArtist(cueSheet.Performer); + if (artistFromCue != null) + { + cueSheetInfo.IdOverrides.Artist = artistFromCue; + } + } + + var parsedAlbumInfo = new ParsedAlbumInfo + { + AlbumTitle = cueSheet.Title, + ArtistName = artistFromCue.Name, + ReleaseDate = cueSheet.Date, + }; + + var albumsFromCue = _parsingService.GetAlbums(parsedAlbumInfo, artistFromCue); + if (albumsFromCue != null && albumsFromCue.Count > 0) + { + cueSheetInfo.IdOverrides.Album = albumsFromCue[0]; + } + + cueSheetInfo.MusicFiles = musicFiles.Where(musicFile => cueSheet.Files.Any(musicFileFromCue => musicFileFromCue.Name == musicFile.Name)).ToList(); + + return cueSheetInfo; + } + } +} diff --git a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs index 4c71e5f49..3504b619b 100644 --- a/src/NzbDrone.Core/MediaFiles/DiskScanService.cs +++ b/src/NzbDrone.Core/MediaFiles/DiskScanService.cs @@ -43,6 +43,7 @@ namespace NzbDrone.Core.MediaFiles private readonly IDiskProvider _diskProvider; private readonly IMediaFileService _mediaFileService; private readonly IMakeImportDecision _importDecisionMaker; + private readonly ICueSheetService _cueSheetService; private readonly IImportApprovedTracks _importApprovedTracks; private readonly IArtistService _artistService; private readonly IMediaFileTableCleanupService _mediaFileTableCleanupService; @@ -54,6 +55,7 @@ namespace NzbDrone.Core.MediaFiles IDiskProvider diskProvider, IMediaFileService mediaFileService, IMakeImportDecision importDecisionMaker, + ICueSheetService cueSheetService, IImportApprovedTracks importApprovedTracks, IArtistService artistService, IRootFolderService rootFolderService, @@ -65,6 +67,7 @@ namespace NzbDrone.Core.MediaFiles _diskProvider = diskProvider; _mediaFileService = mediaFileService; _importDecisionMaker = importDecisionMaker; + _cueSheetService = cueSheetService; _importApprovedTracks = importApprovedTracks; _artistService = artistService; _mediaFileTableCleanupService = mediaFileTableCleanupService; @@ -98,46 +101,8 @@ namespace NzbDrone.Core.MediaFiles }; var decisions = new List>(); - var cueFiles = mediaFileList.Where(x => x.Extension.Equals(".cue")).ToList(); - if (cueFiles.Count > 0) - { - mediaFileList.RemoveAll(l => cueFiles.Contains(l)); - var cueSheetInfos = new List(); - foreach (var cueFile in cueFiles) - { - var cueSheetInfo = _importDecisionMaker.GetCueSheetInfo(cueFile, mediaFileList); - cueSheetInfos.Add(cueSheetInfo); - } - - var cueSheetInfosGroupedByDiscId = cueSheetInfos.GroupBy(x => x.CueSheet.DiscID).ToList(); - foreach (var cueSheetInfoGroup in cueSheetInfosGroupedByDiscId) - { - var audioFilesForCues = new List(); - foreach (var cueSheetInfo in cueSheetInfoGroup) - { - audioFilesForCues.AddRange(cueSheetInfo.MusicFiles); - } - - decisions.AddRange(_importDecisionMaker.GetImportDecisions(audioFilesForCues, cueSheetInfoGroup.First().IdOverrides, itemInfo, config, cueSheetInfos)); - - foreach (var cueSheetInfo in cueSheetInfos) - { - if (cueSheetInfo.CueSheet != null) - { - decisions.ForEach(item => - { - if (cueSheetInfo.IsForMediaFile(item.Item.Path)) - { - item.Item.CueSheetPath = cueSheetInfo.CueSheet.Path; - } - }); - } - - mediaFileList.RemoveAll(x => cueSheetInfo.MusicFiles.Contains(x)); - } - } - } + decisions.AddRange(_cueSheetService.GetImportDecisions(ref mediaFileList, itemInfo, config)); decisions.AddRange(_importDecisionMaker.GetImportDecisions(mediaFileList, null, itemInfo, config)); decisionsStopwatch.Stop(); diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs index 537919552..b45159e2f 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/Identification/IdentificationService.cs @@ -132,41 +132,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Identification i++; _logger.ProgressInfo($"Identifying album {i}/{releases.Count}"); IdentifyRelease(localRelease, idOverrides, config); - - if (cueSheetInfos != null && localRelease.IsSingleFileRelease) - { - var addedMbTracks = new List(); - localRelease.LocalTracks.ForEach(localTrack => - { - var cueSheetFindResult = cueSheetInfos.Find(x => x.IsForMediaFile(localTrack.Path)); - var cueSheet = cueSheetFindResult?.CueSheet; - if (cueSheet == null) - { - return; - } - - localTrack.Tracks.Clear(); - localRelease.AlbumRelease.Tracks.Value.ForEach(mbTrack => - { - cueSheet.Files[0].Tracks.ForEach(cueTrack => - { - if (!string.Equals(cueTrack.Title, mbTrack.Title, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - if (addedMbTracks.Contains(mbTrack)) - { - return; - } - - mbTrack.IsSingleFileRelease = true; - localTrack.Tracks.Add(mbTrack); - addedMbTracks.Add(mbTrack); - }); - }); - }); - } } watch.Stop(); diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs index 7cd06bd52..2a395a4f3 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/ImportDecisionMaker.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.IO; using System.IO.Abstractions; using System.Linq; using DryIoc.ImTools; @@ -22,8 +21,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport public interface IMakeImportDecision { List> GetImportDecisions(List musicFiles, IdentificationOverrides idOverrides, ImportDecisionMakerInfo itemInfo, ImportDecisionMakerConfig config, List cueSheetInfos = null); - List> GetImportDecisions(List cueSheetInfos, ImportDecisionMakerInfo itemInfo, ImportDecisionMakerConfig config); - CueSheetInfo GetCueSheetInfo(IFileInfo cueFile, List musicFiles); } public class IdentificationOverrides @@ -33,14 +30,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport public AlbumRelease AlbumRelease { get; set; } } - public class CueSheetInfo - { - public List MusicFiles { get; set; } = new List(); - public IdentificationOverrides IdOverrides { get; set; } - public CueSheet CueSheet { get; set; } - public bool IsForMediaFile(string path) => CueSheet != null && CueSheet.Files.Count > 0 && CueSheet.Files.Any(x => Path.GetFileName(path) == x.Name); - } - public class ImportDecisionMakerInfo { public DownloadClientItem DownloadClientItem { get; set; } @@ -92,46 +81,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport _logger = logger; } - public CueSheetInfo GetCueSheetInfo(IFileInfo cueFile, List musicFiles) - { - var cueSheetInfo = new CueSheetInfo(); - var cueSheet = new CueSheet(cueFile); - if (cueSheet == null) - { - return cueSheetInfo; - } - - cueSheetInfo.CueSheet = cueSheet; - cueSheetInfo.IdOverrides = new IdentificationOverrides(); - - Artist artistFromCue = null; - if (!cueSheet.Performer.Empty()) - { - artistFromCue = _parsingService.GetArtist(cueSheet.Performer); - if (artistFromCue != null) - { - cueSheetInfo.IdOverrides.Artist = artistFromCue; - } - } - - var parsedAlbumInfo = new ParsedAlbumInfo - { - AlbumTitle = cueSheet.Title, - ArtistName = artistFromCue.Name, - ReleaseDate = cueSheet.Date, - }; - - var albumsFromCue = _parsingService.GetAlbums(parsedAlbumInfo, artistFromCue); - if (albumsFromCue != null && albumsFromCue.Count > 0) - { - cueSheetInfo.IdOverrides.Album = albumsFromCue[0]; - } - - cueSheetInfo.MusicFiles = musicFiles.Where(musicFile => cueSheet.Files.Any(musicFileFromCue => musicFileFromCue.Name == musicFile.Name)).ToList(); - - return cueSheetInfo; - } - public Tuple, List>> GetLocalTracks(List musicFiles, DownloadClientItem downloadClientItem, ParsedAlbumInfo folderInfo, FilterFilesType filter) { var watch = new System.Diagnostics.Stopwatch(); @@ -284,17 +233,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport return decisions; } - public List> GetImportDecisions(List cueSheetInfos, ImportDecisionMakerInfo itemInfo, ImportDecisionMakerConfig config) - { - var decisions = new List>(); - foreach (var cueSheetInfo in cueSheetInfos) - { - decisions.AddRange(GetImportDecisions(cueSheetInfo.MusicFiles, cueSheetInfo.IdOverrides, itemInfo, config, cueSheetInfos)); - } - - return decisions; - } - private void EnsureData(LocalAlbumRelease release) { if (release.AlbumRelease != null && release.AlbumRelease.Album.Value.Artist.Value.QualityProfileId == 0) diff --git a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs index c0edc06ee..c64102657 100644 --- a/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs +++ b/src/NzbDrone.Core/MediaFiles/TrackImport/Manual/ManualImportService.cs @@ -37,6 +37,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual private readonly IRootFolderService _rootFolderService; private readonly IDiskScanService _diskScanService; private readonly IMakeImportDecision _importDecisionMaker; + private readonly ICueSheetService _cueSheetService; private readonly ICustomFormatCalculationService _formatCalculator; private readonly IArtistService _artistService; private readonly IAlbumService _albumService; @@ -55,6 +56,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual IRootFolderService rootFolderService, IDiskScanService diskScanService, IMakeImportDecision importDecisionMaker, + ICueSheetService cueSheetService, ICustomFormatCalculationService formatCalculator, IArtistService artistService, IAlbumService albumService, @@ -73,6 +75,7 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual _rootFolderService = rootFolderService; _diskScanService = diskScanService; _importDecisionMaker = importDecisionMaker; + _cueSheetService = cueSheetService; _formatCalculator = formatCalculator; _artistService = artistService; _albumService = albumService; @@ -153,28 +156,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual var audioFiles = _diskScanService.GetAudioFiles(folder).ToList(); var results = new List(); - // Split cue and non-cue files - var cueFiles = audioFiles.Where(x => x.Extension.Equals(".cue")).ToList(); - if (cueFiles.Count > 0) - { - audioFiles.RemoveAll(l => cueFiles.Contains(l)); - var cueSheetInfos = new List(); - foreach (var cueFile in cueFiles) - { - var cueSheetInfo = _importDecisionMaker.GetCueSheetInfo(cueFile, audioFiles); - cueSheetInfos.Add(cueSheetInfo); - } - - var cueSheetInfosGroupedByDiscId = cueSheetInfos.GroupBy(x => x.CueSheet.DiscID).ToList(); - foreach (var cueSheetInfoGroup in cueSheetInfosGroupedByDiscId) - { - var manualImportItems = ProcessFolder(downloadId, filter, replaceExistingFiles, downloadClientItem, cueSheetInfoGroup.ToList()); - results.AddRange(manualImportItems); - - RemoveProcessedAudioFiles(audioFiles, cueSheetInfos, manualImportItems); - } - } - var idOverrides = new IdentificationOverrides { Artist = artist, @@ -186,36 +167,6 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual return results; } - private void RemoveProcessedAudioFiles(List audioFiles, List cueSheetInfos, List manualImportItems) - { - foreach (var cueSheetInfo in cueSheetInfos) - { - if (cueSheetInfo.CueSheet != null) - { - manualImportItems.ForEach(item => - { - if (cueSheetInfo.IsForMediaFile(item.Path)) - { - item.CueSheetPath = cueSheetInfo.CueSheet.Path; - } - }); - } - - audioFiles.RemoveAll(x => cueSheetInfo.MusicFiles.Contains(x)); - } - } - - private List ProcessFolder(string downloadId, FilterFilesType filter, bool replaceExistingFiles, DownloadClientItem downloadClientItem, List cueSheetInfos) - { - var audioFilesForCues = new List(); - foreach (var cueSheetInfo in cueSheetInfos) - { - audioFilesForCues.AddRange(cueSheetInfo.MusicFiles); - } - - return ProcessFolder(downloadId, cueSheetInfos[0].IdOverrides, filter, replaceExistingFiles, downloadClientItem, cueSheetInfos[0].CueSheet.Title, audioFilesForCues, cueSheetInfos); - } - private List ProcessFolder(string downloadId, IdentificationOverrides idOverrides, FilterFilesType filter, bool replaceExistingFiles, DownloadClientItem downloadClientItem, string albumTitle, List audioFiles, List cueSheetInfos = null) { idOverrides ??= new IdentificationOverrides(); @@ -234,7 +185,11 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual AddNewArtists = false }; - var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, idOverrides, itemInfo, config, cueSheetInfos); + var decisions = _cueSheetService.GetImportDecisions(ref audioFiles, itemInfo, config); + if (audioFiles.Count > 0) + { + decisions.AddRange(_importDecisionMaker.GetImportDecisions(audioFiles, idOverrides, itemInfo, config, cueSheetInfos)); + } // paths will be different for new and old files which is why we need to map separately var newFiles = audioFiles.Join(decisions, @@ -280,40 +235,32 @@ namespace NzbDrone.Core.MediaFiles.TrackImport.Manual { var file = _diskProvider.GetFileInfo(item.Path); audioFiles.Add(file); - } - var cueSheetInfos = new List(); - var audioFilesForCues = new List(); - var itemInfo = new ImportDecisionMakerInfo(); - foreach (var item in group) - { - if (item.IsSingleFileRelease) + if (item.CueSheetPath != null) { var cueFile = _diskProvider.GetFileInfo(item.CueSheetPath); - var cueSheetInfo = _importDecisionMaker.GetCueSheetInfo(cueFile, audioFiles); - cueSheetInfos.Add(cueSheetInfo); - audioFilesForCues.AddRange(cueSheetInfo.MusicFiles); + audioFiles.Add(cueFile); } } - if (audioFilesForCues.Count > 0) - { - var idOverrides = cueSheetInfos.Count > 0 ? cueSheetInfos[0].IdOverrides : null; - var singleFileReleaseDecisions = _importDecisionMaker.GetImportDecisions(audioFilesForCues, idOverrides, itemInfo, config, cueSheetInfos); - var manualImportItems = UpdateItems(group, singleFileReleaseDecisions, replaceExistingFiles, disableReleaseSwitching); - result.AddRange(manualImportItems); - - RemoveProcessedAudioFiles(audioFiles, cueSheetInfos, manualImportItems); - } - + var itemInfo = new ImportDecisionMakerInfo(); var idOverride = new IdentificationOverrides { Artist = group.First().Artist, Album = group.First().Album, AlbumRelease = group.First().Release }; - var decisions = _importDecisionMaker.GetImportDecisions(audioFiles, idOverride, itemInfo, config); - result.AddRange(UpdateItems(group, decisions, replaceExistingFiles, disableReleaseSwitching)); + + var decisions = _cueSheetService.GetImportDecisions(ref audioFiles, itemInfo, config); + if (audioFiles.Count > 0) + { + decisions.AddRange(_importDecisionMaker.GetImportDecisions(audioFiles, idOverride, itemInfo, config)); + } + + if (decisions.Count > 0) + { + result.AddRange(UpdateItems(group, decisions, replaceExistingFiles, disableReleaseSwitching)); + } } return result;