diff --git a/src/Lidarr.Api.V1/Indexers/ReleaseResource.cs b/src/Lidarr.Api.V1/Indexers/ReleaseResource.cs index fc6ce77a2..073bedd90 100644 --- a/src/Lidarr.Api.V1/Indexers/ReleaseResource.cs +++ b/src/Lidarr.Api.V1/Indexers/ReleaseResource.cs @@ -25,7 +25,7 @@ namespace Lidarr.Api.V1.Indexers public string SubGroup { get; set; } public string ReleaseHash { get; set; } public string Title { get; set; } - public bool FullSeason { get; set; } + public bool Discography { get; set; } public bool SceneSource { get; set; } public Language Language { get; set; } public string AirDate { get; set; } @@ -84,7 +84,7 @@ namespace Lidarr.Api.V1.Indexers Language = parsedAlbumInfo.Language, ArtistName = parsedAlbumInfo.ArtistName, AlbumTitle = parsedAlbumInfo.AlbumTitle, - + Discography = parsedAlbumInfo.Discography, Approved = model.Approved, TemporarilyRejected = model.TemporarilyRejected, Rejected = model.Rejected, diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/DiscographySpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/DiscographySpecificationFixture.cs new file mode 100644 index 000000000..f12bbaf00 --- /dev/null +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/DiscographySpecificationFixture.cs @@ -0,0 +1,74 @@ +using System; +using NUnit.Framework; +using NzbDrone.Core.DecisionEngine.Specifications; +using NzbDrone.Core.Parser.Model; +using NzbDrone.Core.Test.Framework; +using FizzWare.NBuilder; +using System.Linq; +using FluentAssertions; +using NzbDrone.Core.Music; +using Moq; +using System.Collections.Generic; + +namespace NzbDrone.Core.Test.DecisionEngineTests +{ + [TestFixture] + public class DiscographySpecificationFixture : CoreTest + { + private RemoteAlbum _remoteAlbum; + + [SetUp] + public void Setup() + { + var artist = Builder.CreateNew().With(s => s.Id = 1234).Build(); + _remoteAlbum = new RemoteAlbum + { + ParsedAlbumInfo = new ParsedAlbumInfo + { + Discography = true + }, + Albums = Builder.CreateListOfSize(3) + .All() + .With(e => e.ReleaseDate = DateTime.UtcNow.AddDays(-8)) + .With(s => s.ArtistId = artist.Id) + .BuildList(), + Artist = artist, + Release = new ReleaseInfo + { + Title = "Artist.Discography.1978.2005.FLAC-RlsGrp" + } + }; + + Mocker.GetMock().Setup(s => s.AlbumsBetweenDates(It.IsAny(), It.IsAny(), false)) + .Returns(new List()); + } + + [Test] + public void should_return_true_if_is_not_a_discography() + { + _remoteAlbum.ParsedAlbumInfo.Discography = false; + _remoteAlbum.Albums.Last().ReleaseDate = DateTime.UtcNow.AddDays(+2); + Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_true_if_all_albums_have_released() + { + Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue(); + } + + [Test] + public void should_return_false_if_one_album_has_not_released() + { + _remoteAlbum.Albums.Last().ReleaseDate = DateTime.UtcNow.AddDays(+2); + Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse(); + } + + [Test] + public void should_return_false_if_an_album_does_not_have_an_release_date() + { + _remoteAlbum.Albums.Last().ReleaseDate = null; + Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse(); + } + } +} diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/MonitoredAlbumSpecificationFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/MonitoredAlbumSpecificationFixture.cs index b23692e4a..5c069fbfa 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/MonitoredAlbumSpecificationFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/MonitoredAlbumSpecificationFixture.cs @@ -124,5 +124,17 @@ namespace NzbDrone.Core.Test.DecisionEngineTests WithFirstAlbumUnmonitored(); _monitoredAlbumSpecification.IsSatisfiedBy(_parseResultSingle, new AlbumSearchCriteria{ MonitoredEpisodesOnly = true}).Accepted.Should().BeFalse(); } + + [Test] + public void should_return_false_if_all_albums_are_not_monitored_for_discography_pack_release() + { + WithSecondAlbumUnmonitored(); + _parseResultMulti.ParsedAlbumInfo = new ParsedAlbumInfo() + { + Discography = true + }; + + _monitoredAlbumSpecification.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs index d4fc3b1ac..d47e35072 100644 --- a/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs +++ b/src/NzbDrone.Core.Test/DecisionEngineTests/PrioritizeDownloadDecisionFixture.cs @@ -137,7 +137,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests } [Test] - public void should_not_throw_if_no_episodes_are_found() + public void should_not_throw_if_no_albums_are_found() { var remoteAlbum1 = GivenRemoteAlbum(new List { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), Language.English, size: 500.Megabytes()); var remoteAlbum2 = GivenRemoteAlbum(new List { GivenAlbum(1) }, new QualityModel(Quality.MP3_256), Language.English, size: 500.Megabytes()); @@ -183,6 +183,38 @@ namespace NzbDrone.Core.Test.DecisionEngineTests qualifiedReports.First().RemoteAlbum.Release.DownloadProtocol.Should().Be(DownloadProtocol.Torrent); } + [Test] + public void should_prefer_discography_pack_above_single_album() + { + var remoteAlbum1 = GivenRemoteAlbum(new List { GivenAlbum(1), GivenAlbum(2) }, new QualityModel(Quality.FLAC), Language.English); + var remoteAlbum2 = GivenRemoteAlbum(new List { GivenAlbum(1) }, new QualityModel(Quality.FLAC), Language.English); + + remoteAlbum1.ParsedAlbumInfo.Discography = true; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteAlbum1)); + decisions.Add(new DownloadDecision(remoteAlbum2)); + + var qualifiedReports = Subject.PrioritizeDecisions(decisions); + qualifiedReports.First().RemoteAlbum.ParsedAlbumInfo.Discography.Should().BeTrue(); + } + + [Test] + public void should_prefer_quality_over_discography_pack() + { + var remoteAlbum1 = GivenRemoteAlbum(new List { GivenAlbum(1), GivenAlbum(2) }, new QualityModel(Quality.MP3_320), Language.English); + var remoteAlbum2 = GivenRemoteAlbum(new List { GivenAlbum(1) }, new QualityModel(Quality.FLAC), Language.English); + + remoteAlbum1.ParsedAlbumInfo.Discography = true; + + var decisions = new List(); + decisions.Add(new DownloadDecision(remoteAlbum1)); + decisions.Add(new DownloadDecision(remoteAlbum2)); + + var qualifiedReports = Subject.PrioritizeDecisions(decisions); + qualifiedReports.First().RemoteAlbum.ParsedAlbumInfo.Discography.Should().BeFalse(); + } + [Test] public void should_prefer_single_album_over_multi_album() { diff --git a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj index 89912fe14..1503491b2 100644 --- a/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj +++ b/src/NzbDrone.Core.Test/NzbDrone.Core.Test.csproj @@ -129,6 +129,7 @@ + diff --git a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs index cc0cf8174..7392d6757 100644 --- a/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs +++ b/src/NzbDrone.Core.Test/ParserTests/ParserFixture.cs @@ -80,7 +80,7 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("Ricardo Arjona - APNEA (Single 2014) (320 kbps)", "Ricardo Arjona", "APNEA")] [TestCase("Kehlani - SweetSexySavage (Deluxe Edition) (2017) 320", "Kehlani", "SweetSexySavage")] [TestCase("Anderson Paak - Malibu (320)(2016)", "Anderson Paak", "Malibu")] - [TestCase("Caetano Veloso Discografia Completa MP3 @256", "Caetano Veloso", "", true)] + [TestCase("Caetano Veloso Discografia Completa MP3 @256", "Caetano Veloso", "Discography", true)] [TestCase("Little Mix - Salute [Deluxe Edition] [2013] [M4A-256]-V3nom [GLT", "Little Mix", "Salute")] [TestCase("Ricky Martin - A Quien Quiera Escuchar (2015) 256 kbps [GloDLS]", "Ricky Martin", "A Quien Quiera Escuchar")] [TestCase("Jake Bugg - Jake Bugg (Album) [2012] {MP3 256 kbps}", "Jake Bugg", "Jake Bugg")] @@ -113,7 +113,9 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("New.Edition-One.Love-CD-FLAC-2017-MrFlac", "New Edition", "One Love")] [TestCase("David_Gray-The_Best_of_David_Gray-(Deluxe_Edition)-2CD-2016-MTD", "David Gray", "The Best of David Gray")] [TestCase("Shinedown-Us and Them-NMR-2005-NMR", "Shinedown", "Us and Them")] - [TestCase("Captain-Discography_1998_-_2001-CD-FLAC-2007-UTP", "Captain", "", true)] + [TestCase("Led Zeppelin - Studio Discography 1969-1982 (10 albums)(flac)", "Led Zeppelin", "Discography", true)] + [TestCase("Minor Threat - Complete Discography [1989] [Anthology]", "Minor Threat", "Discography", true)] + [TestCase("Captain-Discography_1998_-_2001-CD-FLAC-2007-UTP", "Captain", "Discography", true)] [TestCase("Coolio - Gangsta's Paradise (1995) (FLAC Lossless)", "Coolio", "Gangsta's Paradise")] // ruTracker [TestCase("(Eclectic Progressive Rock) [CD] Peter Hammill - From The Trees - 2017, FLAC (tracks + .cue), lossless", "Peter Hammill","From The Trees")] @@ -121,10 +123,10 @@ namespace NzbDrone.Core.Test.ParserTests [TestCase("(Zeuhl / Progressive Rock) [WEB] Dai Kaht - Dai Kaht - 2017, FLAC (tracks), lossless", "Dai Kaht", "Dai Kaht")] //[TestCase("(Industrial Folk) Bumblebee(Shmely, AntiVirus) - Discography, 23 albums - 1998-2011, FLAC(image + .cue), lossless")] //[TestCase("(Heavy Metal) Sergey Mavrin(Mavrik) - Discography(14 CD) [1998-2010], FLAC(image + .cue), lossless")] - [TestCase("(Heavy Metal) [CD] Black Obelisk - Discography - 1991-2015 (36 releases, 32 CDs), FLAC(image + .cue), lossless", "Black Obelisk", "", true)] + [TestCase("(Heavy Metal) [CD] Black Obelisk - Discography - 1991-2015 (36 releases, 32 CDs), FLAC(image + .cue), lossless", "Black Obelisk", "Discography", true)] //[TestCase("(R'n'B / Soul) Moyton - One of the Sta(2014) + Ocean(2014), MP3, 320 kbps", "Moyton", "")] - [TestCase("(Heavy Metal) Aria - Discography(46 CD) [1985 - 2015], FLAC(image + .cue), lossless", "Aria", "", true)] - [TestCase("(Heavy Metal) [CD] Forces United - Discography(6 CDs), 2014-2016, FLAC(image + .cue), lossless", "Forces United", "", true)] + [TestCase("(Heavy Metal) Aria - Discography(46 CD) [1985 - 2015], FLAC(image + .cue), lossless", "Aria", "Discography", true)] + [TestCase("(Heavy Metal) [CD] Forces United - Discography(6 CDs), 2014-2016, FLAC(image + .cue), lossless", "Forces United", "Discography", true)] public void should_parse_artist_name_and_album_title(string postTitle, string name, string title, bool discography = false) { @@ -146,6 +148,20 @@ namespace NzbDrone.Core.Test.ParserTests parseResult.AlbumTitle.ToLowerInvariant().Should().Be("black sabbath"); } + [TestCase("Captain-Discography_1998_-_2001-CD-FLAC-2007-UTP", 1998, 2001)] + [TestCase("(Heavy Metal) Aria - Discography(46 CD) [1985 - 2015]", 1985, 2015)] + [TestCase("Led Zeppelin - Studio Discography 1969-1982 (10 albums)(flac)", 1969, 1982)] + [TestCase("Minor Threat - Complete Discography [1989] [Anthology]", 0, 1989)] + [TestCase("Caetano Veloso Discografia Completa MP3 @256", 0, 0)] + public void should_parse_year_or_year_range_from_discography(string releaseTitle, int startyear, + int endyear) + { + var parseResult = Parser.Parser.ParseAlbumTitle(releaseTitle); + parseResult.Discography.Should().BeTrue(); + parseResult.DiscographyStart.Should().Be(startyear); + parseResult.DiscographyEnd.Should().Be(endyear); + } + [Test] public void should_not_parse_artist_name_and_album_title_by_incorrect_search_criteria() { diff --git a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs index 19d7f6c85..5176b0225 100644 --- a/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs +++ b/src/NzbDrone.Core/DecisionEngine/DownloadDecisionComparer.cs @@ -80,6 +80,14 @@ namespace NzbDrone.Core.DecisionEngine private int CompareAlbumCount(DownloadDecision x, DownloadDecision y) { + var discographyCompare = CompareBy(x.RemoteAlbum, y.RemoteAlbum, + remoteAlbum => remoteAlbum.ParsedAlbumInfo.Discography); + + if (discographyCompare != 0) + { + return discographyCompare; + } + return CompareByReverse(x.RemoteAlbum, y.RemoteAlbum, remoteAlbum => remoteAlbum.Albums.Count); } diff --git a/src/NzbDrone.Core/DecisionEngine/Specifications/DiscographySpecification.cs b/src/NzbDrone.Core/DecisionEngine/Specifications/DiscographySpecification.cs index a1e79112d..e11ed8761 100644 --- a/src/NzbDrone.Core/DecisionEngine/Specifications/DiscographySpecification.cs +++ b/src/NzbDrone.Core/DecisionEngine/Specifications/DiscographySpecification.cs @@ -2,8 +2,8 @@ using System; using NLog; using NzbDrone.Core.IndexerSearch.Definitions; using NzbDrone.Core.Parser.Model; -using NzbDrone.Common.Extensions; using System.Linq; +using NzbDrone.Common.Extensions; namespace NzbDrone.Core.DecisionEngine.Specifications { @@ -21,7 +21,18 @@ namespace NzbDrone.Core.DecisionEngine.Specifications public virtual Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria) { - throw new NotImplementedException(); + if (subject.ParsedAlbumInfo.Discography) + { + _logger.Debug("Checking if all albums in discography release have released. {0}", subject.Release.Title); + + if (subject.Albums.Any(e => !e.ReleaseDate.HasValue || e.ReleaseDate.Value.After(DateTime.UtcNow))) + { + _logger.Debug("Discography release {0} rejected. All albums haven't released yet.", subject.Release.Title); + return Decision.Reject("Discography release rejected. All albums haven't released yet."); + } + } + + return Decision.Accept(); } } } diff --git a/src/NzbDrone.Core/Music/AlbumRepository.cs b/src/NzbDrone.Core/Music/AlbumRepository.cs index 6e295b3be..9bb7037bf 100644 --- a/src/NzbDrone.Core/Music/AlbumRepository.cs +++ b/src/NzbDrone.Core/Music/AlbumRepository.cs @@ -7,7 +7,6 @@ using System.Collections.Generic; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Languages; using NzbDrone.Core.Qualities; -using Marr.Data.QGen; using NzbDrone.Common.Extensions; namespace NzbDrone.Core.Music @@ -22,6 +21,7 @@ namespace NzbDrone.Core.Music PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec); PagingSpec AlbumsWhereCutoffUnmet(PagingSpec pagingSpec, List qualitiesBelowCutoff, List languagesBelowCutoff); List AlbumsBetweenDates(DateTime startDate, DateTime endDate, bool includeUnmonitored); + List ArtistAlbumsBetweenDates(Artist artist, DateTime startDate, DateTime endDate, bool includeUnmonitored); void SetMonitoredFlat(Album album, bool monitored); void SetMonitored(IEnumerable ids, bool monitored); } @@ -44,7 +44,7 @@ namespace NzbDrone.Core.Music public Album FindById(string foreignAlbumId) { - return Query.Where(s => s.ForeignAlbumId == foreignAlbumId).SingleOrDefault(); + return Query.SingleOrDefault(s => s.ForeignAlbumId == foreignAlbumId); } public PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec) @@ -84,6 +84,23 @@ namespace NzbDrone.Core.Music return query.ToList(); } + public List ArtistAlbumsBetweenDates(Artist artist, DateTime startDate, DateTime endDate, bool includeUnmonitored) + { + var query = Query.Join(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistId == s.Id) + .Where(e => e.ReleaseDate >= startDate) + .AndWhere(e => e.ReleaseDate <= endDate) + .AndWhere(e => e.ArtistId == artist.Id); + + + if (!includeUnmonitored) + { + query.AndWhere(e => e.Monitored) + .AndWhere(e => e.Artist.Monitored); + } + + return query.ToList(); + } + private QueryBuilder GetMissingAlbumsQuery(PagingSpec pagingSpec, DateTime currentTime) { string sortKey; @@ -117,16 +134,6 @@ namespace NzbDrone.Core.Music monitored, BuildReleaseDateCutoffWhereClause(currentTime), sortKey, pagingSpec.ToSortDirection(), pagingSpec.PageSize, pagingSpec.PagingOffset()); return Query.QueryText(query); - - //Use Manual Query until we find a way to "NOT EXIST(SELECT 1 from Tracks WHERE [t2].trackFileId <> 0)" - - //return Query.Join(JoinType.Inner, e => e.Artist, (e, s) => e.ArtistId == s.Id) - // .Where(pagingSpec.FilterExpression) - // .AndWhere(BuildReleaseDateCutoffWhereClause(currentTime)) - // //.Where(t => t.TrackFileId == 0) - // .OrderBy(pagingSpec.OrderByClause(), pagingSpec.ToSortDirection()) - // .Skip(pagingSpec.PagingOffset()) - // .Take(pagingSpec.PageSize); } private int GetMissingAlbumsQueryCount(PagingSpec pagingSpec, DateTime currentTime) @@ -214,17 +221,17 @@ namespace NzbDrone.Core.Music private string BuildLanguageCutoffWhereClause(List languagesBelowCutoff) { - var clauses = new List(); + var clauses = new List(); foreach (var language in languagesBelowCutoff) { foreach (var belowCutoff in language.LanguageIds) { - clauses.Add(String.Format("(Artists.[LanguageProfileId] = {0} AND TrackFiles.[Language] = {1})", language.ProfileId, belowCutoff)); + clauses.Add(string.Format("(Artists.[LanguageProfileId] = {0} AND TrackFiles.[Language] = {1})", language.ProfileId, belowCutoff)); } } - return String.Format("({0})", String.Join(" OR ", clauses)); + return string.Format("({0})", string.Join(" OR ", clauses)); } private string BuildQualityCutoffWhereClause(List qualitiesBelowCutoff) @@ -265,8 +272,7 @@ namespace NzbDrone.Core.Music { cleanTitle = cleanTitle.ToLowerInvariant(); - return Query.Where(s => s.CleanTitle == cleanTitle) - .SingleOrDefault(); + return Query.SingleOrDefault(s => s.CleanTitle == cleanTitle); } public Album FindByTitle(int artistId, string title) @@ -282,13 +288,10 @@ namespace NzbDrone.Core.Music { var cleanArtistName = Parser.Parser.CleanArtistName(artistName); cleanTitle = cleanTitle.ToLowerInvariant(); - var query = Query.Join(JoinType.Inner, album => album.Artist, (album, artist) => album.ArtistId == artist.Id) - .Where(artist => artist.CleanName == cleanArtistName) - .Where(album => album.CleanTitle == cleanTitle); + return Query.Join(JoinType.Inner, album => album.Artist, (album, artist) => album.ArtistId == artist.Id) .Where(artist => artist.CleanName == cleanArtistName) - .Where(album => album.CleanTitle == cleanTitle) - .SingleOrDefault(); + .SingleOrDefault(album => album.CleanTitle == cleanTitle); } } } diff --git a/src/NzbDrone.Core/Music/AlbumService.cs b/src/NzbDrone.Core/Music/AlbumService.cs index 244b32928..0f0a5e817 100644 --- a/src/NzbDrone.Core/Music/AlbumService.cs +++ b/src/NzbDrone.Core/Music/AlbumService.cs @@ -1,15 +1,10 @@ using NLog; using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Music.Events; -using NzbDrone.Core.Organizer; using System; using System.Collections.Generic; using System.Linq; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Parser; -using System.Text; -using System.IO; -using NzbDrone.Common.Extensions; namespace NzbDrone.Core.Music { @@ -31,6 +26,7 @@ namespace NzbDrone.Core.Music void SetMonitored(IEnumerable ids, bool monitored); PagingSpec AlbumsWithoutFiles(PagingSpec pagingSpec); List AlbumsBetweenDates(DateTime start, DateTime end, bool includeUnmonitored); + List ArtistAlbumsBetweenDates(Artist artist, DateTime start, DateTime end, bool includeUnmonitored); void InsertMany(List albums); void UpdateMany(List albums); void DeleteMany(List albums); @@ -43,19 +39,16 @@ namespace NzbDrone.Core.Music private readonly IAlbumRepository _albumRepository; private readonly IEventAggregator _eventAggregator; private readonly ITrackService _trackService; - private readonly IBuildFileNames _fileNameBuilder; private readonly Logger _logger; public AlbumService(IAlbumRepository albumRepository, IEventAggregator eventAggregator, ITrackService trackService, - IBuildFileNames fileNameBuilder, Logger logger) { _albumRepository = albumRepository; _eventAggregator = eventAggregator; _trackService = trackService; - _fileNameBuilder = fileNameBuilder; _logger = logger; } @@ -136,6 +129,13 @@ namespace NzbDrone.Core.Music return albums; } + public List ArtistAlbumsBetweenDates(Artist artist, DateTime start, DateTime end, bool includeUnmonitored) + { + var albums = _albumRepository.ArtistAlbumsBetweenDates(artist, start.ToUniversalTime(), end.ToUniversalTime(), includeUnmonitored); + + return albums; + } + public void InsertMany(List albums) { _albumRepository.InsertMany(albums); diff --git a/src/NzbDrone.Core/Parser/Model/ParsedAlbumInfo.cs b/src/NzbDrone.Core/Parser/Model/ParsedAlbumInfo.cs index 534ec611a..c87cf3c35 100644 --- a/src/NzbDrone.Core/Parser/Model/ParsedAlbumInfo.cs +++ b/src/NzbDrone.Core/Parser/Model/ParsedAlbumInfo.cs @@ -16,6 +16,8 @@ namespace NzbDrone.Core.Parser.Model public QualityModel Quality { get; set; } public string ReleaseDate { get; set; } public bool Discography { get; set; } + public int DiscographyStart { get; set; } + public int DiscographyEnd { get; set; } public Language Language { get; set; } public string ReleaseGroup { get; set; } public string ReleaseHash { get; set; } diff --git a/src/NzbDrone.Core/Parser/Parser.cs b/src/NzbDrone.Core/Parser/Parser.cs index e364ce108..21b912376 100644 --- a/src/NzbDrone.Core/Parser/Parser.cs +++ b/src/NzbDrone.Core/Parser/Parser.cs @@ -44,16 +44,28 @@ namespace NzbDrone.Core.Parser private static readonly Regex[] ReportAlbumTitleRegex = new[] { //ruTracker - (Genre) [Source]? Artist - Discography - new Regex(@"^(?:\(.+?\))(?:\W*(?:\[(?.+?)\]))?\W*(?.+?)(?: - )(?Discography|Discografia)", + new Regex(@"^(?:\(.+?\))(?:\W*(?:\[(?.+?)\]))?\W*(?.+?)(?: - )(?Discography|Discografia).+?(?\d{4}).+?(?\d{4})", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Artist - Discography with two years + new Regex(@"^(?.+?)(?: - )(?:.+?)?(?Discography|Discografia).+?(?\d{4}).+?(?\d{4})", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + + //Artist - Discography with end year + new Regex(@"^(?.+?)(?: - )(?:.+?)?(?Discography|Discografia).+?(?\d{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled), //Artist Discography with two years new Regex(@"^(?.+?)\W*(?Discography|Discografia).+?(?\d{4}).+?(?\d{4})", RegexOptions.IgnoreCase | RegexOptions.Compiled), + //Artist Discography with end year + new Regex(@"^(?.+?)\W*(?Discography|Discografia).+?(?\d{4})", + RegexOptions.IgnoreCase | RegexOptions.Compiled), + //Artist Discography - new Regex(@"^(?.+?)\W*(?Discography|Discografia)", - RegexOptions.IgnoreCase | RegexOptions.Compiled), + new Regex(@"^(?.+?)\W*(?Discography|Discografia)", + RegexOptions.IgnoreCase | RegexOptions.Compiled), //ruTracker - (Genre) [Source]? Artist - Album - Year new Regex(@"^(?:\(.+?\))(?:\W*(?:\[(?.+?)\]))?\W*(?.+?)(?: - )(?.+?)(?: - )(?\d{4})", @@ -760,7 +772,23 @@ namespace NzbDrone.Core.Parser if (matchCollection[0].Groups["discography"].Success) { + int discStart; + int discEnd; + int.TryParse(matchCollection[0].Groups["startyear"].Value, out discStart); + int.TryParse(matchCollection[0].Groups["endyear"].Value, out discEnd); result.Discography = true; + + if (discStart > 0 && discEnd > 0) + { + result.DiscographyStart = discStart; + result.DiscographyEnd = discEnd; + } + else if (discEnd > 0) + { + result.DiscographyEnd = discEnd; + } + + result.AlbumTitle = "Discography"; } Logger.Debug("Album Parsed. {0}", result); diff --git a/src/NzbDrone.Core/Parser/ParsingService.cs b/src/NzbDrone.Core/Parser/ParsingService.cs index 63752d127..484873a6f 100644 --- a/src/NzbDrone.Core/Parser/ParsingService.cs +++ b/src/NzbDrone.Core/Parser/ParsingService.cs @@ -119,6 +119,25 @@ namespace NzbDrone.Core.Parser Album albumInfo = null; + if (parsedAlbumInfo.Discography) + { + if (parsedAlbumInfo.DiscographyStart > 0) + { + return _albumService.ArtistAlbumsBetweenDates(artist, + new DateTime(parsedAlbumInfo.DiscographyStart, 1, 1), + new DateTime(parsedAlbumInfo.DiscographyEnd, 12, 31), false); + } + + if (parsedAlbumInfo.DiscographyEnd > 0) + { + return _albumService.ArtistAlbumsBetweenDates(artist, + new DateTime(1800, 1, 1), + new DateTime(parsedAlbumInfo.DiscographyEnd, 12, 31), false); + } + + return _albumService.GetAlbumsByArtist(artist.Id); + } + if (searchCriteria != null) { albumInfo = searchCriteria.Albums.SingleOrDefault(e => e.Title == albumTitle);