New: Already Imported Decision Specification (#661)
parent
d552770da9
commit
61cf1ccb7c
@ -0,0 +1,172 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Qualities;
|
||||
using NzbDrone.Core.Music;
|
||||
using NzbDrone.Core.DecisionEngine.Specifications;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Languages;
|
||||
|
||||
namespace NzbDrone.Core.Test.DecisionEngineTests
|
||||
{
|
||||
[TestFixture]
|
||||
public class AlreadyImportedSpecificationFixture : CoreTest<AlreadyImportedSpecification>
|
||||
{
|
||||
private const int FIRST_ALBUM_ID = 1;
|
||||
private const string TITLE = "Some.Artist-Some.Album-2018-320kbps-CD-Lidarr";
|
||||
|
||||
private Artist _artist;
|
||||
private QualityModel _mp3;
|
||||
private QualityModel _flac;
|
||||
private RemoteAlbum _remoteAlbum;
|
||||
private List<History.History> _history;
|
||||
private TrackFile _firstFile;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
var singleAlbumList = new List<Album>
|
||||
{
|
||||
new Album
|
||||
{
|
||||
Id = FIRST_ALBUM_ID,
|
||||
Title = "Some Album"
|
||||
}
|
||||
};
|
||||
|
||||
_artist = Builder<Artist>.CreateNew()
|
||||
.Build();
|
||||
|
||||
_firstFile = new TrackFile { Quality = new QualityModel(Quality.FLAC, new Revision(version: 2)), DateAdded = DateTime.Now, Language = Language.English };
|
||||
|
||||
_mp3 = new QualityModel(Quality.MP3_320, new Revision(version: 1));
|
||||
_flac = new QualityModel(Quality.FLAC, new Revision(version: 1));
|
||||
|
||||
_remoteAlbum = new RemoteAlbum
|
||||
{
|
||||
Artist = _artist,
|
||||
ParsedAlbumInfo = new ParsedAlbumInfo { Quality = _mp3 },
|
||||
Albums = singleAlbumList,
|
||||
Release = Builder<ReleaseInfo>.CreateNew()
|
||||
.Build()
|
||||
};
|
||||
|
||||
_history = new List<History.History>();
|
||||
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IHistoryService>()
|
||||
.Setup(s => s.GetByAlbum(It.IsAny<int>(), null))
|
||||
.Returns(_history);
|
||||
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>()))
|
||||
.Returns(new List<TrackFile> { _firstFile });
|
||||
}
|
||||
|
||||
private void GivenCdhDisabled()
|
||||
{
|
||||
Mocker.GetMock<IConfigService>()
|
||||
.SetupGet(s => s.EnableCompletedDownloadHandling)
|
||||
.Returns(false);
|
||||
}
|
||||
|
||||
private void GivenHistoryItem(string downloadId, string sourceTitle, QualityModel quality, HistoryEventType eventType)
|
||||
{
|
||||
_history.Add(new History.History
|
||||
{
|
||||
DownloadId = downloadId,
|
||||
SourceTitle = sourceTitle,
|
||||
Quality = quality,
|
||||
Date = DateTime.UtcNow,
|
||||
EventType = eventType
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_CDH_is_disabled()
|
||||
{
|
||||
GivenCdhDisabled();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_album_does_not_have_a_file()
|
||||
{
|
||||
Mocker.GetMock<IMediaFileService>()
|
||||
.Setup(c => c.GetFilesByAlbum(It.IsAny<int>()))
|
||||
.Returns(new List<TrackFile> { });
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_album_does_not_have_grabbed_event()
|
||||
{
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_album_does_not_have_imported_event()
|
||||
{
|
||||
GivenHistoryItem(Guid.NewGuid().ToString().ToUpper(), TITLE, _mp3, HistoryEventType.Grabbed);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_accepted_if_grabbed_and_imported_quality_is_the_same()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.DownloadImported);
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_grabbed_download_id_matches_release_torrent_hash()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _flac, HistoryEventType.DownloadImported);
|
||||
|
||||
_remoteAlbum.Release = Builder<TorrentInfo>.CreateNew()
|
||||
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
|
||||
.With(t => t.InfoHash = downloadId)
|
||||
.Build();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_be_rejected_if_release_title_matches_grabbed_event_source_title()
|
||||
{
|
||||
var downloadId = Guid.NewGuid().ToString().ToUpper();
|
||||
|
||||
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed);
|
||||
GivenHistoryItem(downloadId, TITLE, _flac, HistoryEventType.DownloadImported);
|
||||
|
||||
_remoteAlbum.Release = Builder<TorrentInfo>.CreateNew()
|
||||
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
|
||||
.With(t => t.InfoHash = downloadId)
|
||||
.Build();
|
||||
|
||||
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse();
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,106 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using NLog;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.History;
|
||||
using NzbDrone.Core.Indexers;
|
||||
using NzbDrone.Core.IndexerSearch.Definitions;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.DecisionEngine.Specifications
|
||||
{
|
||||
public class AlreadyImportedSpecification : IDecisionEngineSpecification
|
||||
{
|
||||
private readonly IHistoryService _historyService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly IMediaFileService _mediaFileService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public AlreadyImportedSpecification(IHistoryService historyService,
|
||||
IConfigService configService,
|
||||
IMediaFileService mediaFileService,
|
||||
Logger logger)
|
||||
{
|
||||
_historyService = historyService;
|
||||
_mediaFileService = mediaFileService;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public SpecificationPriority Priority => SpecificationPriority.Database;
|
||||
public RejectionType Type => RejectionType.Permanent;
|
||||
|
||||
public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
|
||||
{
|
||||
var cdhEnabled = _configService.EnableCompletedDownloadHandling;
|
||||
|
||||
if (!cdhEnabled)
|
||||
{
|
||||
_logger.Debug("Skipping already imported check because CDH is disabled");
|
||||
return Decision.Accept();
|
||||
}
|
||||
|
||||
_logger.Debug("Performing already imported check on report");
|
||||
foreach (var album in subject.Albums)
|
||||
{
|
||||
var trackFiles = _mediaFileService.GetFilesByAlbum(album.Id);
|
||||
|
||||
if (trackFiles.Count() == 0)
|
||||
{
|
||||
_logger.Debug("Skipping already imported check for album without files");
|
||||
continue;
|
||||
}
|
||||
|
||||
var historyForAlbum = _historyService.GetByAlbum(album.Id, null);
|
||||
var lastGrabbed = historyForAlbum.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed);
|
||||
|
||||
if (lastGrabbed == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var imported = historyForAlbum.FirstOrDefault(h =>
|
||||
h.EventType == HistoryEventType.DownloadImported &&
|
||||
h.DownloadId == lastGrabbed.DownloadId);
|
||||
|
||||
if (imported == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is really only a guard against redownloading the same release over
|
||||
// and over when the grabbed and imported qualities do not match, if they do
|
||||
// match skip this check.
|
||||
if (lastGrabbed.Quality.Equals(imported.Quality))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var release = subject.Release;
|
||||
|
||||
if (release.DownloadProtocol == DownloadProtocol.Torrent)
|
||||
{
|
||||
var torrentInfo = release as TorrentInfo;
|
||||
|
||||
if (torrentInfo != null && torrentInfo.InfoHash.ToUpper() == lastGrabbed.DownloadId)
|
||||
{
|
||||
_logger.Debug("Has same torrent hash as a grabbed and imported release");
|
||||
return Decision.Reject("Has same torrent hash as a grabbed and imported release");
|
||||
}
|
||||
}
|
||||
|
||||
// Only based on title because a release with the same title on another indexer/released at
|
||||
// a different time very likely has the exact same content and we don't need to also try it.
|
||||
|
||||
if (release.Title.Equals(lastGrabbed.SourceTitle, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
_logger.Debug("Has same release name as a grabbed and imported release");
|
||||
return Decision.Reject("Has same release name as a grabbed and imported release");
|
||||
}
|
||||
}
|
||||
|
||||
return Decision.Accept();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in new issue