Track fully imported downloads in separate history table

New: Improved detection of already imported downloads
Closes #232
pull/1392/head
Mark McDowall 4 years ago committed by ta264
parent e1eb9a0ba7
commit 25b37ace34

@ -23,8 +23,6 @@ function getIconName(eventType) {
return icons.RETAG; return icons.RETAG;
case 'bookImportIncomplete': case 'bookImportIncomplete':
return icons.DOWNLOADED; return icons.DOWNLOADED;
case 'downloadImported':
return icons.DOWNLOADED;
case 'downloadIgnored': case 'downloadIgnored':
return icons.IGNORE; return icons.IGNORE;
default: default:
@ -61,8 +59,6 @@ function getTooltip(eventType, data) {
return 'Book file tags updated'; return 'Book file tags updated';
case 'bookImportIncomplete': case 'bookImportIncomplete':
return 'Files downloaded but not all could be imported'; return 'Files downloaded but not all could be imported';
case 'downloadImported':
return 'Download completed and successfully imported';
case 'downloadIgnored': case 'downloadIgnored':
return 'Book Download Ignored'; return 'Book Download Ignored';
default: default:

@ -131,17 +131,6 @@ export const defaultState = {
} }
] ]
}, },
{
key: 'downloadImported',
label: 'Download Imported',
filters: [
{
key: 'eventType',
value: '8',
type: filterTypes.EQUAL
}
]
},
{ {
key: 'deleted', key: 'deleted',
label: 'Deleted', label: 'Deleted',

@ -4,6 +4,7 @@ using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Books; using NzbDrone.Core.Books;
using NzbDrone.Core.History;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -29,21 +30,21 @@ namespace NzbDrone.Core.Test.Datastore
{ {
var quality = new QualityModel { Quality = Quality.MP3, Revision = new Revision(version: 2) }; var quality = new QualityModel { Quality = Quality.MP3, Revision = new Revision(version: 2) };
var history = Builder<History.History>.CreateNew() var history = Builder<EntityHistory>.CreateNew()
.With(c => c.Id = 0) .With(c => c.Id = 0)
.With(c => c.Quality = quality) .With(c => c.Quality = quality)
.Build(); .Build();
Db.Insert(history); Db.Insert(history);
var loadedQuality = Db.Single<History.History>().Quality; var loadedQuality = Db.Single<EntityHistory>().Quality;
loadedQuality.Should().Be(quality); loadedQuality.Should().Be(quality);
} }
[Test] [Test]
public void embedded_list_of_document_with_json() public void embedded_list_of_document_with_json()
{ {
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.All().With(c => c.Id = 0) .All().With(c => c.Id = 0)
.Build().ToList(); .Build().ToList();
@ -52,7 +53,7 @@ namespace NzbDrone.Core.Test.Datastore
Db.InsertMany(history); Db.InsertMany(history);
var returnedHistory = Db.All<History.History>(); var returnedHistory = Db.All<EntityHistory>();
returnedHistory[0].Quality.Quality.Should().Be(Quality.MP3); returnedHistory[0].Quality.Quality.Should().Be(Quality.MP3);
} }

@ -26,7 +26,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private QualityModel _mp3; private QualityModel _mp3;
private QualityModel _flac; private QualityModel _flac;
private RemoteBook _remoteBook; private RemoteBook _remoteBook;
private List<History.History> _history; private List<EntityHistory> _history;
private BookFile _firstFile; private BookFile _firstFile;
[SetUp] [SetUp]
@ -58,7 +58,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Build() .Build()
}; };
_history = new List<History.History>(); _history = new List<EntityHistory>();
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
.SetupGet(s => s.EnableCompletedDownloadHandling) .SetupGet(s => s.EnableCompletedDownloadHandling)
@ -80,9 +80,9 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Returns(false); .Returns(false);
} }
private void GivenHistoryItem(string downloadId, string sourceTitle, QualityModel quality, HistoryEventType eventType) private void GivenHistoryItem(string downloadId, string sourceTitle, QualityModel quality, EntityHistoryEventType eventType)
{ {
_history.Add(new History.History _history.Add(new EntityHistory
{ {
DownloadId = downloadId, DownloadId = downloadId,
SourceTitle = sourceTitle, SourceTitle = sourceTitle,
@ -119,7 +119,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_accepted_if_book_does_not_have_imported_event() public void should_be_accepted_if_book_does_not_have_imported_event()
{ {
GivenHistoryItem(Guid.NewGuid().ToString().ToUpper(), TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(Guid.NewGuid().ToString().ToUpper(), TITLE, _mp3, EntityHistoryEventType.Grabbed);
Subject.IsSatisfiedBy(_remoteBook, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteBook, null).Accepted.Should().BeTrue();
} }
@ -129,8 +129,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
var downloadId = Guid.NewGuid().ToString().ToUpper(); var downloadId = Guid.NewGuid().ToString().ToUpper();
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(downloadId, TITLE, _mp3, EntityHistoryEventType.Grabbed);
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.DownloadImported); GivenHistoryItem(downloadId, TITLE, _mp3, EntityHistoryEventType.BookFileImported);
Subject.IsSatisfiedBy(_remoteBook, null).Accepted.Should().BeTrue(); Subject.IsSatisfiedBy(_remoteBook, null).Accepted.Should().BeTrue();
} }
@ -140,8 +140,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
var downloadId = Guid.NewGuid().ToString().ToUpper(); var downloadId = Guid.NewGuid().ToString().ToUpper();
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(downloadId, TITLE, _mp3, EntityHistoryEventType.Grabbed);
GivenHistoryItem(downloadId, TITLE, _flac, HistoryEventType.DownloadImported); GivenHistoryItem(downloadId, TITLE, _flac, EntityHistoryEventType.BookFileImported);
_remoteBook.Release = Builder<TorrentInfo>.CreateNew() _remoteBook.Release = Builder<TorrentInfo>.CreateNew()
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent) .With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
@ -156,8 +156,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
var downloadId = Guid.NewGuid().ToString().ToUpper(); var downloadId = Guid.NewGuid().ToString().ToUpper();
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(downloadId, TITLE, _mp3, EntityHistoryEventType.Grabbed);
GivenHistoryItem(downloadId, TITLE, _flac, HistoryEventType.DownloadImported); GivenHistoryItem(downloadId, TITLE, _flac, EntityHistoryEventType.BookFileImported);
_remoteBook.Release = Builder<TorrentInfo>.CreateNew() _remoteBook.Release = Builder<TorrentInfo>.CreateNew()
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent) .With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
@ -170,8 +170,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_be_accepted_if_release_torrent_hash_is_null_and_downloadId_is_null() public void should_be_accepted_if_release_torrent_hash_is_null_and_downloadId_is_null()
{ {
GivenHistoryItem(null, TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(null, TITLE, _mp3, EntityHistoryEventType.Grabbed);
GivenHistoryItem(null, TITLE, _flac, HistoryEventType.DownloadImported); GivenHistoryItem(null, TITLE, _flac, EntityHistoryEventType.BookFileImported);
_remoteBook.Release = Builder<TorrentInfo>.CreateNew() _remoteBook.Release = Builder<TorrentInfo>.CreateNew()
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent) .With(t => t.DownloadProtocol = DownloadProtocol.Torrent)
@ -186,8 +186,8 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
{ {
var downloadId = Guid.NewGuid().ToString().ToUpper(); var downloadId = Guid.NewGuid().ToString().ToUpper();
GivenHistoryItem(downloadId, TITLE, _mp3, HistoryEventType.Grabbed); GivenHistoryItem(downloadId, TITLE, _mp3, EntityHistoryEventType.Grabbed);
GivenHistoryItem(downloadId, TITLE, _flac, HistoryEventType.DownloadImported); GivenHistoryItem(downloadId, TITLE, _flac, EntityHistoryEventType.BookFileImported);
_remoteBook.Release = Builder<TorrentInfo>.CreateNew() _remoteBook.Release = Builder<TorrentInfo>.CreateNew()
.With(t => t.DownloadProtocol = DownloadProtocol.Torrent) .With(t => t.DownloadProtocol = DownloadProtocol.Torrent)

@ -76,10 +76,10 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
.Returns(true); .Returns(true);
} }
private void GivenMostRecentForBook(int bookId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType) private void GivenMostRecentForBook(int bookId, string downloadId, QualityModel quality, DateTime date, EntityHistoryEventType eventType)
{ {
Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForBook(bookId)) Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForBook(bookId))
.Returns(new History.History { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType }); .Returns(new EntityHistory { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType });
} }
private void GivenCdhDisabled() private void GivenCdhDisabled()
@ -98,66 +98,66 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_true_if_latest_history_item_is_null() public void should_return_true_if_latest_history_item_is_null()
{ {
Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForBook(It.IsAny<int>())).Returns((History.History)null); Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForBook(It.IsAny<int>())).Returns((EntityHistory)null);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }
[Test] [Test]
public void should_return_true_if_latest_history_item_is_not_grabbed() public void should_return_true_if_latest_history_item_is_not_grabbed()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, HistoryEventType.DownloadFailed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, EntityHistoryEventType.DownloadFailed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }
// [Test] // [Test]
// public void should_return_true_if_latest_history_has_a_download_id_and_cdh_is_enabled() // public void should_return_true_if_latest_history_has_a_download_id_and_cdh_is_enabled()
// { // {
// GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); // GivenMostRecentForEpisode(FIRST_EPISODE_ID, "test", _notupgradableQuality, DateTime.UtcNow, EpisodeHistoryEventType.Grabbed);
// _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); // _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
// } // }
[Test] [Test]
public void should_return_true_if_latest_history_item_is_older_than_twelve_hours() public void should_return_true_if_latest_history_item_is_older_than_twelve_hours()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow.AddHours(-12).AddMilliseconds(-1), HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow.AddHours(-12).AddMilliseconds(-1), EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }
[Test] [Test]
public void should_be_upgradable_if_only_book_is_upgradable() public void should_be_upgradable_if_only_book_is_upgradable()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeTrue();
} }
[Test] [Test]
public void should_be_upgradable_if_both_books_are_upgradable() public void should_be_upgradable_if_both_books_are_upgradable()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }
[Test] [Test]
public void should_not_be_upgradable_if_both_books_are_not_upgradable() public void should_not_be_upgradable_if_both_books_are_not_upgradable()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
} }
[Test] [Test]
public void should_be_not_upgradable_if_only_first_books_is_upgradable() public void should_be_not_upgradable_if_only_first_books_is_upgradable()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
} }
[Test] [Test]
public void should_be_not_upgradable_if_only_second_books_is_upgradable() public void should_be_not_upgradable_if_only_second_books_is_upgradable()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(SECOND_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
} }
@ -168,7 +168,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1)); _parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1)); _upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1));
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
} }
@ -180,7 +180,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1)); _parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1)); _upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1));
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _upgradableQuality, DateTime.UtcNow, EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
} }
@ -188,7 +188,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
[Test] [Test]
public void should_return_false_if_latest_history_item_is_only_one_hour_old() public void should_return_false_if_latest_history_item_is_only_one_hour_old()
{ {
GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow.AddHours(-1), HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, string.Empty, _notupgradableQuality, DateTime.UtcNow.AddHours(-1), EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeFalse();
} }
@ -196,7 +196,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_false_if_latest_history_has_a_download_id_and_cdh_is_disabled() public void should_return_false_if_latest_history_has_a_download_id_and_cdh_is_disabled()
{ {
GivenCdhDisabled(); GivenCdhDisabled();
GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }
@ -208,7 +208,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
_parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1)); _parseResultSingle.ParsedBookInfo.Quality = new QualityModel(Quality.MP3, new Revision(version: 1));
_upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1)); _upgradableQuality = new QualityModel(Quality.MP3, new Revision(version: 1));
GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _upgradableQuality, DateTime.UtcNow.AddDays(-100), EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
} }
@ -217,7 +217,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
public void should_return_false_if_only_book_is_not_upgradable_and_cdh_is_disabled() public void should_return_false_if_only_book_is_not_upgradable_and_cdh_is_disabled()
{ {
GivenCdhDisabled(); GivenCdhDisabled();
GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _notupgradableQuality, DateTime.UtcNow.AddDays(-100), HistoryEventType.Grabbed); GivenMostRecentForBook(FIRST_ALBUM_ID, "test", _notupgradableQuality, DateTime.UtcNow.AddDays(-100), EntityHistoryEventType.Grabbed);
_upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse(); _upgradeHistory.IsSatisfiedBy(_parseResultSingle, null).Accepted.Should().BeFalse();
} }
} }

@ -52,7 +52,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId)) .Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId))
.Returns(new History.History()); .Returns(new EntityHistory());
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.GetAuthor("Drone.S01E01.HDTV")) .Setup(s => s.GetAuthor("Drone.S01E01.HDTV"))
@ -83,7 +83,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
_trackedDownload.DownloadItem.Title = "Droned Pilot"; // Set a badly named download _trackedDownload.DownloadItem.Title = "Droned Pilot"; // Set a badly named download
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.MostRecentForDownloadId(It.Is<string>(i => i == "1234"))) .Setup(s => s.MostRecentForDownloadId(It.Is<string>(i => i == "1234")))
.Returns(new History.History() { SourceTitle = "Droned S01E01" }); .Returns(new EntityHistory() { SourceTitle = "Droned S01E01" });
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.GetAuthor(It.IsAny<string>())) .Setup(s => s.GetAuthor(It.IsAny<string>()))
@ -194,6 +194,10 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv".AsOsAgnostic() }), "Test Failure") new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv".AsOsAgnostic() }), "Test Failure")
}); });
Mocker.GetMock<IHistoryService>()
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
.Returns(new List<EntityHistory>());
Subject.Import(_trackedDownload); Subject.Import(_trackedDownload);
AssertImported(); AssertImported();
@ -220,7 +224,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv".AsOsAgnostic() }), "Test Failure") new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv".AsOsAgnostic() }), "Test Failure")
}); });
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.BuildList(); .BuildList();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
@ -252,7 +256,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv" }), "Test Failure") new ImportResult(new ImportDecision<LocalBook>(new LocalBook { Path = @"C:\TestPath\Droned.S01E01.mkv" }), "Test Failure")
}); });
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.BuildList(); .BuildList();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
@ -260,7 +264,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
.Returns(history); .Returns(history);
Mocker.GetMock<ITrackedDownloadAlreadyImported>() Mocker.GetMock<ITrackedDownloadAlreadyImported>()
.Setup(s => s.IsImported(It.IsAny<TrackedDownload>(), It.IsAny<List<History.History>>())) .Setup(s => s.IsImported(It.IsAny<TrackedDownload>(), It.IsAny<List<EntityHistory>>()))
.Returns(false); .Returns(false);
Subject.Import(_trackedDownload); Subject.Import(_trackedDownload);
@ -314,7 +318,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
new LocalBook { Path = @"C:\TestPath\Droned.S01E02.mkv", Book = books[1] }), "Test Failure") new LocalBook { Path = @"C:\TestPath\Droned.S01E02.mkv", Book = books[1] }), "Test Failure")
}); });
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.BuildList(); .BuildList();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
@ -322,7 +326,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
.Returns(history); .Returns(history);
Mocker.GetMock<ITrackedDownloadAlreadyImported>() Mocker.GetMock<ITrackedDownloadAlreadyImported>()
.Setup(s => s.IsImported(It.IsAny<TrackedDownload>(), It.IsAny<List<History.History>>())) .Setup(s => s.IsImported(It.IsAny<TrackedDownload>(), It.IsAny<List<EntityHistory>>()))
.Returns(true); .Returns(true);
Subject.Import(_trackedDownload); Subject.Import(_trackedDownload);

@ -51,7 +51,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId)) .Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId))
.Returns(new History.History()); .Returns(new EntityHistory());
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.GetAuthor("Drone.S01E01.HDTV")) .Setup(s => s.GetAuthor("Drone.S01E01.HDTV"))
@ -71,7 +71,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
{ {
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId)) .Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId))
.Returns((History.History)null); .Returns((EntityHistory)null);
} }
private void GivenAuthorMatch() private void GivenAuthorMatch()
@ -87,7 +87,7 @@ namespace NzbDrone.Core.Test.Download.CompletedDownloadServiceTests
_trackedDownload.DownloadItem.Title = "Droned Pilot"; // Set a badly named download _trackedDownload.DownloadItem.Title = "Droned Pilot"; // Set a badly named download
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.MostRecentForDownloadId(It.Is<string>(i => i == "1234"))) .Setup(s => s.MostRecentForDownloadId(It.Is<string>(i => i == "1234")))
.Returns(new History.History() { SourceTitle = "Droned S01E01" }); .Returns(new EntityHistory() { SourceTitle = "Droned S01E01" });
Mocker.GetMock<IParsingService>() Mocker.GetMock<IParsingService>()
.Setup(s => s.GetAuthor(It.IsAny<string>())) .Setup(s => s.GetAuthor(It.IsAny<string>()))

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
public class ProcessFailedFixture : CoreTest<FailedDownloadService> public class ProcessFailedFixture : CoreTest<FailedDownloadService>
{ {
private TrackedDownload _trackedDownload; private TrackedDownload _trackedDownload;
private List<History.History> _grabHistory; private List<EntityHistory> _grabHistory;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
.With(h => h.Title = "Drone.S01E01.HDTV") .With(h => h.Title = "Drone.S01E01.HDTV")
.Build(); .Build();
_grabHistory = Builder<History.History>.CreateListOfSize(2).BuildList(); _grabHistory = Builder<EntityHistory>.CreateListOfSize(2).BuildList();
var remoteBook = new RemoteBook var remoteBook = new RemoteBook
{ {
@ -45,7 +45,7 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
.Build(); .Build();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed)) .Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed))
.Returns(_grabHistory); .Returns(_grabHistory);
} }

@ -19,7 +19,7 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
public class ProcessFixture : CoreTest<FailedDownloadService> public class ProcessFixture : CoreTest<FailedDownloadService>
{ {
private TrackedDownload _trackedDownload; private TrackedDownload _trackedDownload;
private List<History.History> _grabHistory; private List<EntityHistory> _grabHistory;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
.With(h => h.Title = "Drone.DroneTheBook.FLAC") .With(h => h.Title = "Drone.DroneTheBook.FLAC")
.Build(); .Build();
_grabHistory = Builder<History.History>.CreateListOfSize(2).BuildList(); _grabHistory = Builder<EntityHistory>.CreateListOfSize(2).BuildList();
var remoteBook = new RemoteBook var remoteBook = new RemoteBook
{ {
@ -45,15 +45,15 @@ namespace NzbDrone.Core.Test.Download.FailedDownloadServiceTests
.Build(); .Build();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed)) .Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed))
.Returns(_grabHistory); .Returns(_grabHistory);
} }
private void GivenNoGrabbedHistory() private void GivenNoGrabbedHistory()
{ {
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed)) .Setup(s => s.Find(_trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed))
.Returns(new List<History.History>()); .Returns(new List<EntityHistory>());
} }
[Test] [Test]

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
private List<Book> _books; private List<Book> _books;
private TrackedDownload _trackedDownload; private TrackedDownload _trackedDownload;
private List<History.History> _historyItems; private List<EntityHistory> _historyItems;
[SetUp] [SetUp]
public void Setup() public void Setup()
@ -30,7 +30,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
.With(t => t.RemoteBook = remoteBook) .With(t => t.RemoteBook = remoteBook)
.Build(); .Build();
_historyItems = new List<History.History>(); _historyItems = new List<EntityHistory>();
} }
public void GivenEpisodes(int count) public void GivenEpisodes(int count)
@ -39,12 +39,12 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
.BuildList()); .BuildList());
} }
public void GivenHistoryForEpisode(Book episode, params HistoryEventType[] eventTypes) public void GivenHistoryForEpisode(Book episode, params EntityHistoryEventType[] eventTypes)
{ {
foreach (var eventType in eventTypes) foreach (var eventType in eventTypes)
{ {
_historyItems.Add( _historyItems.Add(
Builder<History.History>.CreateNew() Builder<EntityHistory>.CreateNew()
.With(h => h.BookId = episode.Id) .With(h => h.BookId = episode.Id)
.With(h => h.EventType = eventType) .With(h => h.EventType = eventType)
.Build()); .Build());
@ -66,7 +66,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
GivenEpisodes(1); GivenEpisodes(1);
GivenHistoryForEpisode(_books[0], HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[0], EntityHistoryEventType.Grabbed);
Subject.IsImported(_trackedDownload, _historyItems) Subject.IsImported(_trackedDownload, _historyItems)
.Should() .Should()
@ -78,8 +78,8 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
GivenEpisodes(2); GivenEpisodes(2);
GivenHistoryForEpisode(_books[0], HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[0], EntityHistoryEventType.Grabbed);
GivenHistoryForEpisode(_books[1], HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[1], EntityHistoryEventType.Grabbed);
Subject.IsImported(_trackedDownload, _historyItems) Subject.IsImported(_trackedDownload, _historyItems)
.Should() .Should()
@ -91,8 +91,8 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
GivenEpisodes(2); GivenEpisodes(2);
GivenHistoryForEpisode(_books[0], HistoryEventType.DownloadImported, HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[0], EntityHistoryEventType.BookFileImported, EntityHistoryEventType.Grabbed);
GivenHistoryForEpisode(_books[1], HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[1], EntityHistoryEventType.Grabbed);
Subject.IsImported(_trackedDownload, _historyItems) Subject.IsImported(_trackedDownload, _historyItems)
.Should() .Should()
@ -104,7 +104,7 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
GivenEpisodes(1); GivenEpisodes(1);
GivenHistoryForEpisode(_books[0], HistoryEventType.DownloadImported, HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[0], EntityHistoryEventType.BookFileImported, EntityHistoryEventType.Grabbed);
Subject.IsImported(_trackedDownload, _historyItems) Subject.IsImported(_trackedDownload, _historyItems)
.Should() .Should()
@ -116,8 +116,8 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
GivenEpisodes(2); GivenEpisodes(2);
GivenHistoryForEpisode(_books[0], HistoryEventType.DownloadImported, HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[0], EntityHistoryEventType.BookFileImported, EntityHistoryEventType.Grabbed);
GivenHistoryForEpisode(_books[1], HistoryEventType.DownloadImported, HistoryEventType.Grabbed); GivenHistoryForEpisode(_books[1], EntityHistoryEventType.BookFileImported, EntityHistoryEventType.Grabbed);
Subject.IsImported(_trackedDownload, _historyItems) Subject.IsImported(_trackedDownload, _historyItems)
.Should() .Should()

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using FluentAssertions; using FluentAssertions;
using Moq; using Moq;
@ -22,15 +22,15 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
{ {
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(s => s.FindByDownloadId(It.Is<string>(sr => sr == "35238"))) .Setup(s => s.FindByDownloadId(It.Is<string>(sr => sr == "35238")))
.Returns(new List<History.History>() .Returns(new List<EntityHistory>()
{ {
new History.History() new EntityHistory()
{ {
DownloadId = "35238", DownloadId = "35238",
SourceTitle = "Audio Author - Audio Book [2018 - FLAC]", SourceTitle = "Audio Author - Audio Book [2018 - FLAC]",
AuthorId = 5, AuthorId = 5,
BookId = 4, BookId = 4,
} }
}); });
} }

@ -71,6 +71,10 @@ namespace NzbDrone.Core.Test.HealthCheck.Checks
.Setup(s => s.GetDownloadClients()) .Setup(s => s.GetDownloadClients())
.Returns(new IDownloadClient[] { _downloadClient.Object }); .Returns(new IDownloadClient[] { _downloadClient.Object });
Mocker.GetMock<IProvideDownloadClient>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(_downloadClient.Object);
Mocker.GetMock<IConfigService>() Mocker.GetMock<IConfigService>()
.Setup(s => s.EnableCompletedDownloadHandling) .Setup(s => s.EnableCompletedDownloadHandling)
.Returns(true); .Returns(true);

@ -8,12 +8,12 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.HistoryTests namespace NzbDrone.Core.Test.HistoryTests
{ {
[TestFixture] [TestFixture]
public class HistoryRepositoryFixture : DbTest<HistoryRepository, History.History> public class HistoryRepositoryFixture : DbTest<HistoryRepository, EntityHistory>
{ {
[Test] [Test]
public void should_read_write_dictionary() public void should_read_write_dictionary()
{ {
var history = Builder<History.History>.CreateNew() var history = Builder<EntityHistory>.CreateNew()
.With(c => c.Quality = new QualityModel()) .With(c => c.Quality = new QualityModel())
.BuildNew(); .BuildNew();
@ -28,16 +28,16 @@ namespace NzbDrone.Core.Test.HistoryTests
[Test] [Test]
public void should_get_download_history() public void should_get_download_history()
{ {
var historyBluray = Builder<History.History>.CreateNew() var historyBluray = Builder<EntityHistory>.CreateNew()
.With(c => c.Quality = new QualityModel(Quality.MP3)) .With(c => c.Quality = new QualityModel(Quality.MP3))
.With(c => c.AuthorId = 12) .With(c => c.AuthorId = 12)
.With(c => c.EventType = HistoryEventType.Grabbed) .With(c => c.EventType = EntityHistoryEventType.Grabbed)
.BuildNew(); .BuildNew();
var historyDvd = Builder<History.History>.CreateNew() var historyDvd = Builder<EntityHistory>.CreateNew()
.With(c => c.Quality = new QualityModel(Quality.AZW3)) .With(c => c.Quality = new QualityModel(Quality.AZW3))
.With(c => c.AuthorId = 12) .With(c => c.AuthorId = 12)
.With(c => c.EventType = HistoryEventType.Grabbed) .With(c => c.EventType = EntityHistoryEventType.Grabbed)
.BuildNew(); .BuildNew();
Subject.Insert(historyBluray); Subject.Insert(historyBluray);

@ -1,6 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
@ -69,7 +68,7 @@ namespace NzbDrone.Core.Test.HistoryTests
Subject.Handle(new TrackImportedEvent(localTrack, trackFile, new List<BookFile>(), true, downloadClientItem)); Subject.Handle(new TrackImportedEvent(localTrack, trackFile, new List<BookFile>(), true, downloadClientItem));
Mocker.GetMock<IHistoryRepository>() Mocker.GetMock<IHistoryRepository>()
.Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localTrack.Path)))); .Verify(v => v.Insert(It.Is<EntityHistory>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localTrack.Path))));
} }
} }
} }

@ -1,7 +1,8 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.Books; using NzbDrone.Core.Books;
using NzbDrone.Core.History;
using NzbDrone.Core.Housekeeping.Housekeepers; using NzbDrone.Core.Housekeeping.Housekeepers;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
@ -9,7 +10,7 @@ using NzbDrone.Core.Test.Framework;
namespace NzbDrone.Core.Test.Housekeeping.Housekeepers namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{ {
[TestFixture] [TestFixture]
public class CleanupOrphanedHistoryItemsFixture : DbTest<CleanupOrphanedHistoryItems, History.History> public class CleanupOrphanedHistoryItemsFixture : DbTest<CleanupOrphanedHistoryItems, EntityHistory>
{ {
private Author _author; private Author _author;
private Book _book; private Book _book;
@ -39,7 +40,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{ {
GivenBook(); GivenBook();
var history = Builder<History.History>.CreateNew() var history = Builder<EntityHistory>.CreateNew()
.With(h => h.Quality = new QualityModel()) .With(h => h.Quality = new QualityModel())
.With(h => h.BookId = _book.Id) .With(h => h.BookId = _book.Id)
.BuildNew(); .BuildNew();
@ -54,7 +55,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
{ {
GivenAuthor(); GivenAuthor();
var history = Builder<History.History>.CreateNew() var history = Builder<EntityHistory>.CreateNew()
.With(h => h.Quality = new QualityModel()) .With(h => h.Quality = new QualityModel())
.With(h => h.AuthorId = _author.Id) .With(h => h.AuthorId = _author.Id)
.BuildNew(); .BuildNew();
@ -70,7 +71,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
GivenAuthor(); GivenAuthor();
GivenBook(); GivenBook();
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.All() .All()
.With(h => h.Quality = new QualityModel()) .With(h => h.Quality = new QualityModel())
.With(h => h.BookId = _book.Id) .With(h => h.BookId = _book.Id)
@ -91,7 +92,7 @@ namespace NzbDrone.Core.Test.Housekeeping.Housekeepers
GivenAuthor(); GivenAuthor();
GivenBook(); GivenBook();
var history = Builder<History.History>.CreateListOfSize(2) var history = Builder<EntityHistory>.CreateListOfSize(2)
.All() .All()
.With(h => h.Quality = new QualityModel()) .With(h => h.Quality = new QualityModel())
.With(h => h.AuthorId = _author.Id) .With(h => h.AuthorId = _author.Id)

@ -73,8 +73,8 @@ namespace NzbDrone.Core.Test.MusicTests
.Returns(new List<BookFile>()); .Returns(new List<BookFile>());
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(x => x.GetByAuthor(It.IsAny<int>(), It.IsAny<HistoryEventType?>())) .Setup(x => x.GetByAuthor(It.IsAny<int>(), It.IsAny<EntityHistoryEventType?>()))
.Returns(new List<History.History>()); .Returns(new List<EntityHistory>());
Mocker.GetMock<IImportListExclusionService>() Mocker.GetMock<IImportListExclusionService>()
.Setup(x => x.FindByForeignId(It.IsAny<List<string>>())) .Setup(x => x.FindByForeignId(It.IsAny<List<string>>()))

@ -51,12 +51,12 @@ namespace NzbDrone.Core.Test.QueueTests
.Build() .Build()
.ToList(); .ToList();
var historyItem = Builder<History.History>.CreateNew() var historyItem = Builder<EntityHistory>.CreateNew()
.Build(); .Build();
Mocker.GetMock<IHistoryService>() Mocker.GetMock<IHistoryService>()
.Setup(c => c.Find(It.IsAny<string>(), HistoryEventType.Grabbed)).Returns( .Setup(c => c.Find(It.IsAny<string>(), EntityHistoryEventType.Grabbed)).Returns(
new List<History.History> { historyItem }); new List<EntityHistory> { historyItem });
} }
[Test] [Test]

@ -30,7 +30,7 @@ namespace NzbDrone.Core.Analytics
{ {
get get
{ {
var lastRecord = _historyService.Paged(new PagingSpec<History.History>() { Page = 0, PageSize = 1, SortKey = "date", SortDirection = SortDirection.Descending }); var lastRecord = _historyService.Paged(new PagingSpec<EntityHistory>() { Page = 0, PageSize = 1, SortKey = "date", SortDirection = SortDirection.Descending });
var monthAgo = DateTime.UtcNow.AddMonths(-1); var monthAgo = DateTime.UtcNow.AddMonths(-1);
return lastRecord.Records.Any(v => v.Date > monthAgo); return lastRecord.Records.Any(v => v.Date > monthAgo);

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Data;
using FluentMigrator;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(20)]
public class AddDownloadHistory : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
Create.TableForModel("DownloadHistory")
.WithColumn("EventType").AsInt32().NotNullable()
.WithColumn("AuthorId").AsInt32().NotNullable()
.WithColumn("DownloadId").AsString().NotNullable()
.WithColumn("SourceTitle").AsString().NotNullable()
.WithColumn("Date").AsDateTime().NotNullable()
.WithColumn("Protocol").AsInt32().Nullable()
.WithColumn("IndexerId").AsInt32().Nullable()
.WithColumn("DownloadClientId").AsInt32().Nullable()
.WithColumn("Release").AsString().Nullable()
.WithColumn("Data").AsString().Nullable();
Create.Index().OnTable("DownloadHistory").OnColumn("EventType");
Create.Index().OnTable("DownloadHistory").OnColumn("SeriesId");
Create.Index().OnTable("DownloadHistory").OnColumn("DownloadId");
Execute.WithConnection(InitialImportedDownloadHistory);
Execute.Sql("DELETE From History where EventType = 8;");
}
private static readonly Dictionary<int, int> EventTypeMap = new Dictionary<int, int>()
{
// EpisodeHistoryType.Grabbed -> DownloadHistoryType.Grabbed
{ 1, 1 },
// EpisodeHistoryType.DownloadFolderImported -> DownloadHistoryType.DownloadImported
{ 8, 2 },
// EpisodeHistoryType.DownloadFailed -> DownloadHistoryType.DownloadFailed
{ 4, 3 },
// EpisodeHistoryType.DownloadIgnored -> DownloadHistoryType.DownloadIgnored
{ 10, 4 },
// EpisodeHistoryType.DownloadImportIncomplete -> DownloadHistoryType.DownloadImportIncomplete
{ 7, 6 }
};
private void InitialImportedDownloadHistory(IDbConnection conn, IDbTransaction tran)
{
using (var cmd = conn.CreateCommand())
{
cmd.Transaction = tran;
cmd.CommandText = "SELECT AuthorId, DownloadId, EventType, SourceTitle, Date, Data FROM History WHERE DownloadId IS NOT NULL AND EventType IN (1, 8, 4, 10, 7) GROUP BY EventType, DownloadId";
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
var seriesId = reader.GetInt32(0);
var downloadId = reader.GetString(1);
var eventType = reader.GetInt32(2);
var sourceTitle = reader.GetString(3);
var date = reader.GetDateTime(4);
var rawData = reader.GetString(5);
var data = Json.Deserialize<Dictionary<string, string>>(rawData);
var downloadHistoryEventType = EventTypeMap[eventType];
var protocol = data.ContainsKey("protocol") ? Convert.ToInt32(data["protocol"]) : (int?)null;
var downloadHistoryData = new Dictionary<string, string>();
if (data.ContainsKey("indexer"))
{
downloadHistoryData.Add("indexer", data["indexer"]);
}
if (data.ContainsKey("downloadClient"))
{
downloadHistoryData.Add("downloadClient", data["downloadClient"]);
}
using (var updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = @"INSERT INTO DownloadHistory (EventType, AuthorId, DownloadId, SourceTitle, Date, Protocol, Data) VALUES (?, ?, ?, ?, ?, ?, ?)";
updateCmd.AddParameter(downloadHistoryEventType);
updateCmd.AddParameter(seriesId);
updateCmd.AddParameter(downloadId);
updateCmd.AddParameter(sourceTitle);
updateCmd.AddParameter(date);
updateCmd.AddParameter(protocol);
updateCmd.AddParameter(downloadHistoryData.ToJson());
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}

@ -10,10 +10,12 @@ using NzbDrone.Core.Configuration;
using NzbDrone.Core.CustomFilters; using NzbDrone.Core.CustomFilters;
using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Download; using NzbDrone.Core.Download;
using NzbDrone.Core.Download.History;
using NzbDrone.Core.Download.Pending; using NzbDrone.Core.Download.Pending;
using NzbDrone.Core.Extras.Metadata; using NzbDrone.Core.Extras.Metadata;
using NzbDrone.Core.Extras.Metadata.Files; using NzbDrone.Core.Extras.Metadata.Files;
using NzbDrone.Core.Extras.Others; using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.History;
using NzbDrone.Core.Http; using NzbDrone.Core.Http;
using NzbDrone.Core.ImportLists; using NzbDrone.Core.ImportLists;
using NzbDrone.Core.ImportLists.Exclusions; using NzbDrone.Core.ImportLists.Exclusions;
@ -93,7 +95,7 @@ namespace NzbDrone.Core.Datastore
.Ignore(d => d.Protocol) .Ignore(d => d.Protocol)
.Ignore(d => d.Tags); .Ignore(d => d.Tags);
Mapper.Entity<History.History>("History").RegisterModel(); Mapper.Entity<EntityHistory>("History").RegisterModel();
Mapper.Entity<Author>("Authors") Mapper.Entity<Author>("Authors")
.Ignore(s => s.RootFolderPath) .Ignore(s => s.RootFolderPath)
@ -195,6 +197,8 @@ namespace NzbDrone.Core.Datastore
Mapper.Entity<ImportListExclusion>("ImportListExclusions").RegisterModel(); Mapper.Entity<ImportListExclusion>("ImportListExclusions").RegisterModel();
Mapper.Entity<CachedHttpResponse>("HttpResponse").RegisterModel(); Mapper.Entity<CachedHttpResponse>("HttpResponse").RegisterModel();
Mapper.Entity<DownloadHistory>("DownloadHistory").RegisterModel();
} }
private static void RegisterMappers() private static void RegisterMappers()

@ -53,7 +53,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
} }
var historyForBook = _historyService.GetByBook(book.Id, null); var historyForBook = _historyService.GetByBook(book.Id, null);
var lastGrabbed = historyForBook.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed); var lastGrabbed = historyForBook.FirstOrDefault(h => h.EventType == EntityHistoryEventType.Grabbed);
if (lastGrabbed == null) if (lastGrabbed == null)
{ {
@ -61,7 +61,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications
} }
var imported = historyForBook.FirstOrDefault(h => var imported = historyForBook.FirstOrDefault(h =>
h.EventType == HistoryEventType.DownloadImported && h.EventType == EntityHistoryEventType.BookFileImported &&
h.DownloadId == lastGrabbed.DownloadId); h.DownloadId == lastGrabbed.DownloadId);
if (imported == null) if (imported == null)

@ -51,7 +51,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
_logger.Debug("Checking current status of book [{0}] in history", book.Id); _logger.Debug("Checking current status of book [{0}] in history", book.Id);
var mostRecent = _historyService.MostRecentForBook(book.Id); var mostRecent = _historyService.MostRecentForBook(book.Id);
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed) if (mostRecent != null && mostRecent.EventType == EntityHistoryEventType.Grabbed)
{ {
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12)); var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));

@ -1,4 +1,4 @@
using NzbDrone.Common.Messaging; using NzbDrone.Common.Messaging;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download namespace NzbDrone.Core.Download
@ -6,7 +6,9 @@ namespace NzbDrone.Core.Download
public class BookGrabbedEvent : IEvent public class BookGrabbedEvent : IEvent
{ {
public RemoteBook Book { get; private set; } public RemoteBook Book { get; private set; }
public int DownloadClientId { get; set; }
public string DownloadClient { get; set; } public string DownloadClient { get; set; }
public string DownloadClientName { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }
public BookGrabbedEvent(RemoteBook book) public BookGrabbedEvent(RemoteBook book)

@ -102,6 +102,8 @@ namespace NzbDrone.Core.Download
var bookGrabbedEvent = new BookGrabbedEvent(remoteBook); var bookGrabbedEvent = new BookGrabbedEvent(remoteBook);
bookGrabbedEvent.DownloadClient = downloadClient.Name; bookGrabbedEvent.DownloadClient = downloadClient.Name;
bookGrabbedEvent.DownloadClientId = downloadClient.Definition.Id;
bookGrabbedEvent.DownloadClientName = downloadClient.Definition.Name;
if (!string.IsNullOrWhiteSpace(downloadClientId)) if (!string.IsNullOrWhiteSpace(downloadClientId))
{ {

@ -18,12 +18,15 @@ namespace NzbDrone.Core.Download
public class FailedDownloadService : IFailedDownloadService public class FailedDownloadService : IFailedDownloadService
{ {
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly ITrackedDownloadService _trackedDownloadService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
public FailedDownloadService(IHistoryService historyService, public FailedDownloadService(IHistoryService historyService,
ITrackedDownloadService trackedDownloadService,
IEventAggregator eventAggregator) IEventAggregator eventAggregator)
{ {
_historyService = historyService; _historyService = historyService;
_trackedDownloadService = trackedDownloadService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
} }
@ -34,22 +37,24 @@ namespace NzbDrone.Core.Download
var downloadId = history.DownloadId; var downloadId = history.DownloadId;
if (downloadId.IsNullOrWhiteSpace()) if (downloadId.IsNullOrWhiteSpace())
{ {
PublishDownloadFailedEvent(new List<History.History> { history }, "Manually marked as failed", skipReDownload: skipReDownload); PublishDownloadFailedEvent(new List<EntityHistory> { history }, "Manually marked as failed", skipReDownload: skipReDownload);
} }
else else
{ {
var grabbedHistory = _historyService.Find(downloadId, HistoryEventType.Grabbed).ToList(); var grabbedHistory = _historyService.Find(downloadId, EntityHistoryEventType.Grabbed).ToList();
PublishDownloadFailedEvent(grabbedHistory, "Manually marked as failed"); PublishDownloadFailedEvent(grabbedHistory, "Manually marked as failed");
} }
} }
public void MarkAsFailed(string downloadId, bool skipReDownload = false) public void MarkAsFailed(string downloadId, bool skipReDownload = false)
{ {
var history = _historyService.Find(downloadId, HistoryEventType.Grabbed); var history = _historyService.Find(downloadId, EntityHistoryEventType.Grabbed);
if (history.Any()) if (history.Any())
{ {
PublishDownloadFailedEvent(history, "Manually marked as failed", skipReDownload: skipReDownload); var trackedDownload = _trackedDownloadService.Find(downloadId);
PublishDownloadFailedEvent(history, "Manually marked as failed", trackedDownload, skipReDownload);
} }
} }
@ -65,7 +70,7 @@ namespace NzbDrone.Core.Download
trackedDownload.DownloadItem.Status == DownloadItemStatus.Failed) trackedDownload.DownloadItem.Status == DownloadItemStatus.Failed)
{ {
var grabbedItems = _historyService var grabbedItems = _historyService
.Find(trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed) .Find(trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed)
.ToList(); .ToList();
if (grabbedItems.Empty()) if (grabbedItems.Empty())
@ -86,7 +91,7 @@ namespace NzbDrone.Core.Download
} }
var grabbedItems = _historyService var grabbedItems = _historyService
.Find(trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed) .Find(trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed)
.ToList(); .ToList();
if (grabbedItems.Empty()) if (grabbedItems.Empty())
@ -109,7 +114,7 @@ namespace NzbDrone.Core.Download
PublishDownloadFailedEvent(grabbedItems, failure, trackedDownload); PublishDownloadFailedEvent(grabbedItems, failure, trackedDownload);
} }
private void PublishDownloadFailedEvent(List<History.History> historyItems, string message, TrackedDownload trackedDownload = null, bool skipReDownload = false) private void PublishDownloadFailedEvent(List<EntityHistory> historyItems, string message, TrackedDownload trackedDownload = null, bool skipReDownload = false)
{ {
var historyItem = historyItems.First(); var historyItem = historyItems.First();
@ -119,7 +124,7 @@ namespace NzbDrone.Core.Download
BookIds = historyItems.Select(h => h.BookId).ToList(), BookIds = historyItems.Select(h => h.BookId).ToList(),
Quality = historyItem.Quality, Quality = historyItem.Quality,
SourceTitle = historyItem.SourceTitle, SourceTitle = historyItem.SourceTitle,
DownloadClient = historyItem.Data.GetValueOrDefault(History.History.DOWNLOAD_CLIENT), DownloadClient = historyItem.Data.GetValueOrDefault(EntityHistory.DOWNLOAD_CLIENT),
DownloadId = historyItem.DownloadId, DownloadId = historyItem.DownloadId,
Message = message, Message = message,
Data = historyItem.Data, Data = historyItem.Data,

@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.Download.History
{
public class DownloadHistory : ModelBase
{
public DownloadHistoryEventType EventType { get; set; }
public int AuthorId { get; set; }
public string DownloadId { get; set; }
public string SourceTitle { get; set; }
public DateTime Date { get; set; }
public DownloadProtocol Protocol { get; set; }
public int IndexerId { get; set; }
public int DownloadClientId { get; set; }
public ReleaseInfo Release { get; set; }
public Dictionary<string, string> Data { get; set; }
public DownloadHistory()
{
Data = new Dictionary<string, string>();
}
}
public enum DownloadHistoryEventType
{
DownloadGrabbed = 1,
DownloadImported = 2,
DownloadFailed = 3,
DownloadIgnored = 4,
FileImported = 5,
DownloadImportIncomplete = 6
}
}

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.Linq;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Download.History
{
public interface IDownloadHistoryRepository : IBasicRepository<DownloadHistory>
{
List<DownloadHistory> FindByDownloadId(string downloadId);
void DeleteByAuthorId(int authorId);
}
public class DownloadHistoryRepository : BasicRepository<DownloadHistory>, IDownloadHistoryRepository
{
public DownloadHistoryRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator)
{
}
public List<DownloadHistory> FindByDownloadId(string downloadId)
{
return Query(h => h.DownloadId == downloadId)
.OrderByDescending(h => h.Date)
.ToList();
}
public void DeleteByAuthorId(int authorId)
{
Delete(r => r.AuthorId == authorId);
}
}
}

@ -0,0 +1,240 @@
using System;
using System.IO;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Books.Events;
using NzbDrone.Core.History;
using NzbDrone.Core.MediaFiles.Events;
using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.Download.History
{
public interface IDownloadHistoryService
{
bool DownloadAlreadyImported(string downloadId);
DownloadHistory GetLatestDownloadHistoryItem(string downloadId);
}
public class DownloadHistoryService : IDownloadHistoryService,
IHandle<BookGrabbedEvent>,
IHandle<TrackImportedEvent>,
IHandle<BookImportIncompleteEvent>,
IHandle<DownloadCompletedEvent>,
IHandle<DownloadFailedEvent>,
IHandle<DownloadIgnoredEvent>,
IHandle<AuthorDeletedEvent>
{
private readonly IDownloadHistoryRepository _repository;
private readonly IHistoryService _historyService;
public DownloadHistoryService(IDownloadHistoryRepository repository, IHistoryService historyService)
{
_repository = repository;
_historyService = historyService;
}
public bool DownloadAlreadyImported(string downloadId)
{
var events = _repository.FindByDownloadId(downloadId);
// Events are ordered by date descending, if a grabbed event comes before an imported event then it was never imported
// or grabbed again after importing and should be reprocessed.
foreach (var e in events)
{
if (e.EventType == DownloadHistoryEventType.DownloadGrabbed)
{
return false;
}
if (e.EventType == DownloadHistoryEventType.DownloadImported)
{
return true;
}
}
return false;
}
public DownloadHistory GetLatestDownloadHistoryItem(string downloadId)
{
var events = _repository.FindByDownloadId(downloadId);
// Events are ordered by date descending. We'll return the most recent expected event.
foreach (var e in events)
{
if (e.EventType == DownloadHistoryEventType.DownloadGrabbed)
{
return e;
}
if (e.EventType == DownloadHistoryEventType.DownloadImported)
{
return e;
}
if (e.EventType == DownloadHistoryEventType.DownloadFailed)
{
return e;
}
if (e.EventType == DownloadHistoryEventType.DownloadImportIncomplete)
{
return e;
}
}
return null;
}
public void Handle(BookGrabbedEvent message)
{
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.DownloadGrabbed,
AuthorId = message.Book.Author.Id,
DownloadId = message.DownloadId,
SourceTitle = message.Book.Release.Title,
Date = DateTime.UtcNow,
Protocol = message.Book.Release.DownloadProtocol,
IndexerId = message.Book.Release.IndexerId,
DownloadClientId = message.DownloadClientId,
Release = message.Book.Release
};
history.Data.Add("Indexer", message.Book.Release.Indexer);
history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("DownloadClientName", message.DownloadClientName);
history.Data.Add("PreferredWordScore", message.Book.PreferredWordScore.ToString());
_repository.Insert(history);
}
public void Handle(TrackImportedEvent message)
{
if (!message.NewDownload)
{
return;
}
var downloadId = message.DownloadId;
// Try to find the downloadId if the user used manual import (from wanted: missing) or the
// API to import and downloadId wasn't provided.
if (downloadId.IsNullOrWhiteSpace())
{
downloadId = _historyService.FindDownloadId(message);
}
if (downloadId.IsNullOrWhiteSpace())
{
return;
}
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.FileImported,
AuthorId = message.BookInfo.Author.Id,
DownloadId = downloadId,
SourceTitle = message.BookInfo.Path,
Date = DateTime.UtcNow,
Protocol = message.DownloadClientInfo.Protocol,
DownloadClientId = message.DownloadClientInfo.Id
};
history.Data.Add("DownloadClient", message.DownloadClientInfo.Type);
history.Data.Add("DownloadClientName", message.DownloadClientInfo.Name);
history.Data.Add("SourcePath", message.BookInfo.Path);
history.Data.Add("DestinationPath", message.ImportedBook.Path);
_repository.Insert(history);
}
public void Handle(BookImportIncompleteEvent message)
{
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.DownloadImportIncomplete,
AuthorId = message.TrackedDownload.RemoteBook?.Author.Id ?? 0,
DownloadId = message.TrackedDownload.DownloadItem.DownloadId,
SourceTitle = message.TrackedDownload.DownloadItem.OutputPath.ToString(),
Date = DateTime.UtcNow,
Protocol = message.TrackedDownload.Protocol,
DownloadClientId = message.TrackedDownload.DownloadClient
};
history.Data.Add("DownloadClient", message.TrackedDownload.DownloadItem.DownloadClientInfo.Type);
history.Data.Add("DownloadClientName", message.TrackedDownload.DownloadItem.DownloadClientInfo.Name);
history.Data.Add("StatusMessages", message.TrackedDownload.StatusMessages.ToJson());
_repository.Insert(history);
}
public void Handle(DownloadCompletedEvent message)
{
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.DownloadImported,
AuthorId = message.TrackedDownload.RemoteBook.Author.Id,
DownloadId = message.TrackedDownload.DownloadItem.DownloadId,
SourceTitle = message.TrackedDownload.DownloadItem.OutputPath.ToString(),
Date = DateTime.UtcNow,
Protocol = message.TrackedDownload.Protocol,
DownloadClientId = message.TrackedDownload.DownloadClient
};
history.Data.Add("DownloadClient", message.TrackedDownload.DownloadItem.DownloadClientInfo.Type);
history.Data.Add("DownloadClientName", message.TrackedDownload.DownloadItem.DownloadClientInfo.Name);
_repository.Insert(history);
}
public void Handle(DownloadFailedEvent message)
{
// Don't track failed download for an unknown download
if (message.TrackedDownload == null)
{
return;
}
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.DownloadFailed,
AuthorId = message.AuthorId,
DownloadId = message.DownloadId,
SourceTitle = message.SourceTitle,
Date = DateTime.UtcNow,
Protocol = message.TrackedDownload.Protocol,
DownloadClientId = message.TrackedDownload.DownloadClient
};
history.Data.Add("DownloadClient", message.TrackedDownload.DownloadItem.DownloadClientInfo.Type);
history.Data.Add("DownloadClientName", message.TrackedDownload.DownloadItem.DownloadClientInfo.Name);
_repository.Insert(history);
}
public void Handle(DownloadIgnoredEvent message)
{
var history = new DownloadHistory
{
EventType = DownloadHistoryEventType.DownloadIgnored,
AuthorId = message.AuthorId,
DownloadId = message.DownloadId,
SourceTitle = message.SourceTitle,
Date = DateTime.UtcNow,
Protocol = message.DownloadClientInfo.Protocol,
DownloadClientId = message.DownloadClientInfo.Id
};
history.Data.Add("DownloadClient", message.DownloadClientInfo.Type);
history.Data.Add("DownloadClientName", message.DownloadClientInfo.Name);
_repository.Insert(history);
}
public void Handle(AuthorDeletedEvent message)
{
_repository.DeleteByAuthorId(message.Author.Id);
}
}
}

@ -8,7 +8,6 @@ namespace NzbDrone.Core.Download
public interface IDownloadClient : IProvider public interface IDownloadClient : IProvider
{ {
DownloadProtocol Protocol { get; } DownloadProtocol Protocol { get; }
string Download(RemoteBook remoteBook); string Download(RemoteBook remoteBook);
IEnumerable<DownloadClientItem> GetItems(); IEnumerable<DownloadClientItem> GetItems();
DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt); DownloadClientItem GetImportItem(DownloadClientItem item, DownloadClientItem previousImportAttempt);

@ -1,4 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.History; using NzbDrone.Core.History;
@ -7,12 +7,12 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{ {
public interface ITrackedDownloadAlreadyImported public interface ITrackedDownloadAlreadyImported
{ {
bool IsImported(TrackedDownload trackedDownload, List<History.History> historyItems); bool IsImported(TrackedDownload trackedDownload, List<EntityHistory> historyItems);
} }
public class TrackedDownloadAlreadyImported : ITrackedDownloadAlreadyImported public class TrackedDownloadAlreadyImported : ITrackedDownloadAlreadyImported
{ {
public bool IsImported(TrackedDownload trackedDownload, List<History.History> historyItems) public bool IsImported(TrackedDownload trackedDownload, List<EntityHistory> historyItems)
{ {
if (historyItems.Empty()) if (historyItems.Empty())
{ {
@ -33,7 +33,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
return false; return false;
} }
return new[] { HistoryEventType.DownloadImported, HistoryEventType.BookFileImported }.Contains(lastHistoryItem.EventType); return lastHistoryItem.EventType == EntityHistoryEventType.BookFileImported;
}); });
return allBooksImportedInHistory; return allBooksImportedInHistory;

@ -7,6 +7,7 @@ using NzbDrone.Common.Extensions;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.Books; using NzbDrone.Core.Books;
using NzbDrone.Core.Books.Events; using NzbDrone.Core.Books.Events;
using NzbDrone.Core.Download.History;
using NzbDrone.Core.History; using NzbDrone.Core.History;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser; using NzbDrone.Core.Parser;
@ -28,7 +29,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
private readonly IParsingService _parsingService; private readonly IParsingService _parsingService;
private readonly IHistoryService _historyService; private readonly IHistoryService _historyService;
private readonly IEventAggregator _eventAggregator; private readonly IEventAggregator _eventAggregator;
private readonly ITrackedDownloadAlreadyImported _trackedDownloadAlreadyImported; private readonly IDownloadHistoryService _downloadHistoryService;
private readonly Logger _logger; private readonly Logger _logger;
private readonly ICached<TrackedDownload> _cache; private readonly ICached<TrackedDownload> _cache;
@ -36,13 +37,13 @@ namespace NzbDrone.Core.Download.TrackedDownloads
ICacheManager cacheManager, ICacheManager cacheManager,
IHistoryService historyService, IHistoryService historyService,
IEventAggregator eventAggregator, IEventAggregator eventAggregator,
ITrackedDownloadAlreadyImported trackedDownloadAlreadyImported, IDownloadHistoryService downloadHistoryService,
Logger logger) Logger logger)
{ {
_parsingService = parsingService; _parsingService = parsingService;
_historyService = historyService; _historyService = historyService;
_eventAggregator = eventAggregator; _eventAggregator = eventAggregator;
_trackedDownloadAlreadyImported = trackedDownloadAlreadyImported; _downloadHistoryService = downloadHistoryService;
_cache = cacheManager.GetCache<TrackedDownload>(GetType()); _cache = cacheManager.GetCache<TrackedDownload>(GetType());
_logger = logger; _logger = logger;
} }
@ -129,31 +130,25 @@ namespace NzbDrone.Core.Download.TrackedDownloads
trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo); trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo);
} }
if (historyItems.Any()) var downloadHistory = _downloadHistoryService.GetLatestDownloadHistoryItem(downloadItem.DownloadId);
{
var firstHistoryItem = historyItems.First();
var state = GetStateFromHistory(firstHistoryItem);
// One potential issue here is if the latest is imported, but other episodes are ignored or never imported.
// It's unlikely that will happen, but could happen if additional episodes are added to season after it's already imported.
if (state == TrackedDownloadState.Imported)
{
var allImported = _trackedDownloadAlreadyImported.IsImported(trackedDownload, historyItems);
trackedDownload.State = allImported ? TrackedDownloadState.Imported : TrackedDownloadState.Downloading; if (downloadHistory != null)
} {
else var state = GetStateFromHistory(downloadHistory.EventType);
{ trackedDownload.State = state;
trackedDownload.State = state;
}
if (firstHistoryItem.EventType == HistoryEventType.BookImportIncomplete) if (downloadHistory.EventType == DownloadHistoryEventType.DownloadImportIncomplete)
{ {
var messages = Json.Deserialize<List<TrackedDownloadStatusMessage>>(firstHistoryItem?.Data["statusMessages"]).ToArray(); var messages = Json.Deserialize<List<TrackedDownloadStatusMessage>>(downloadHistory.Data["statusMessages"]).ToArray();
trackedDownload.Warn(messages); trackedDownload.Warn(messages);
} }
}
if (historyItems.Any())
{
var firstHistoryItem = historyItems.First();
var grabbedEvent = historyItems.FirstOrDefault(v => v.EventType == EntityHistoryEventType.Grabbed);
var grabbedEvent = historyItems.FirstOrDefault(v => v.EventType == HistoryEventType.Grabbed);
trackedDownload.Indexer = grabbedEvent?.Data["indexer"]; trackedDownload.Indexer = grabbedEvent?.Data["indexer"];
if (parsedBookInfo == null || if (parsedBookInfo == null ||
@ -162,7 +157,6 @@ namespace NzbDrone.Core.Download.TrackedDownloads
trackedDownload.RemoteBook.Books.Empty()) trackedDownload.RemoteBook.Books.Empty())
{ {
// Try parsing the original source title and if that fails, try parsing it as a special // Try parsing the original source title and if that fails, try parsing it as a special
// TODO: Pass the TVDB ID and TVRage IDs in as well so we have a better chance for finding the item
var historyAuthor = firstHistoryItem.Author; var historyAuthor = firstHistoryItem.Author;
var historyBooks = new List<Book> { firstHistoryItem.Book }; var historyBooks = new List<Book> { firstHistoryItem.Book };
@ -172,7 +166,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{ {
trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo, trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo,
firstHistoryItem.AuthorId, firstHistoryItem.AuthorId,
historyItems.Where(v => v.EventType == HistoryEventType.Grabbed).Select(h => h.BookId) historyItems.Where(v => v.EventType == EntityHistoryEventType.Grabbed).Select(h => h.BookId)
.Distinct()); .Distinct());
} }
else else
@ -186,7 +180,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
{ {
trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo, trackedDownload.RemoteBook = _parsingService.Map(parsedBookInfo,
firstHistoryItem.AuthorId, firstHistoryItem.AuthorId,
historyItems.Where(v => v.EventType == HistoryEventType.Grabbed).Select(h => h.BookId) historyItems.Where(v => v.EventType == EntityHistoryEventType.Grabbed).Select(h => h.BookId)
.Distinct()); .Distinct());
} }
} }
@ -244,27 +238,21 @@ namespace NzbDrone.Core.Download.TrackedDownloads
} }
} }
private static TrackedDownloadState GetStateFromHistory(NzbDrone.Core.History.History history) private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
{ {
switch (history.EventType) switch (eventType)
{ {
case HistoryEventType.BookImportIncomplete: case DownloadHistoryEventType.DownloadImportIncomplete:
return TrackedDownloadState.ImportFailed; return TrackedDownloadState.ImportFailed;
case HistoryEventType.DownloadImported: case DownloadHistoryEventType.DownloadImported:
return TrackedDownloadState.Imported; return TrackedDownloadState.Imported;
case HistoryEventType.DownloadFailed: case DownloadHistoryEventType.DownloadFailed:
return TrackedDownloadState.DownloadFailed; return TrackedDownloadState.DownloadFailed;
case HistoryEventType.DownloadIgnored: case DownloadHistoryEventType.DownloadIgnored:
return TrackedDownloadState.Ignored; return TrackedDownloadState.Ignored;
default:
return TrackedDownloadState.Downloading;
} }
// Since DownloadComplete is a new event type, we can't assume it exists for old downloads
if (history.EventType == HistoryEventType.BookFileImported)
{
return DateTime.UtcNow.Subtract(history.Date).TotalSeconds < 60 ? TrackedDownloadState.Importing : TrackedDownloadState.Imported;
}
return TrackedDownloadState.Downloading;
} }
public void Handle(BookDeletedEvent message) public void Handle(BookDeletedEvent message)

@ -139,7 +139,7 @@ namespace NzbDrone.Core.HealthCheck.Checks
// If the previous case did not match then the failure occured in DownloadedTracksImportService, // If the previous case did not match then the failure occured in DownloadedTracksImportService,
// while trying to locate the files reported by the download client // while trying to locate the files reported by the download client
var client = _downloadClientProvider.GetDownloadClients().FirstOrDefault(x => x.Definition.Name == failureMessage.DownloadClient); var client = _downloadClientProvider.Get(failureMessage.DownloadClientInfo.Id);
try try
{ {
var status = client.GetStatus(); var status = client.GetStatus();

@ -6,11 +6,11 @@ using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
{ {
public class History : ModelBase public class EntityHistory : ModelBase
{ {
public const string DOWNLOAD_CLIENT = "downloadClient"; public const string DOWNLOAD_CLIENT = "downloadClient";
public History() public EntityHistory()
{ {
Data = new Dictionary<string, string>(); Data = new Dictionary<string, string>();
} }
@ -22,17 +22,16 @@ namespace NzbDrone.Core.History
public DateTime Date { get; set; } public DateTime Date { get; set; }
public Book Book { get; set; } public Book Book { get; set; }
public Author Author { get; set; } public Author Author { get; set; }
public HistoryEventType EventType { get; set; } public EntityHistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }
} }
public enum HistoryEventType public enum EntityHistoryEventType
{ {
Unknown = 0, Unknown = 0,
Grabbed = 1, Grabbed = 1,
AuthorFolderImported = 2,
BookFileImported = 3, BookFileImported = 3,
DownloadFailed = 4, DownloadFailed = 4,
BookFileDeleted = 5, BookFileDeleted = 5,

@ -8,46 +8,46 @@ using NzbDrone.Core.Qualities;
namespace NzbDrone.Core.History namespace NzbDrone.Core.History
{ {
public interface IHistoryRepository : IBasicRepository<History> public interface IHistoryRepository : IBasicRepository<EntityHistory>
{ {
History MostRecentForBook(int bookId); EntityHistory MostRecentForBook(int bookId);
History MostRecentForDownloadId(string downloadId); EntityHistory MostRecentForDownloadId(string downloadId);
List<History> FindByDownloadId(string downloadId); List<EntityHistory> FindByDownloadId(string downloadId);
List<History> GetByAuthor(int authorId, HistoryEventType? eventType); List<EntityHistory> GetByAuthor(int authorId, EntityHistoryEventType? eventType);
List<History> GetByBook(int bookId, HistoryEventType? eventType); List<EntityHistory> GetByBook(int bookId, EntityHistoryEventType? eventType);
List<History> FindDownloadHistory(int idAuthorId, QualityModel quality); List<EntityHistory> FindDownloadHistory(int idAuthorId, QualityModel quality);
void DeleteForAuthor(int authorId); void DeleteForAuthor(int authorId);
List<History> Since(DateTime date, HistoryEventType? eventType); List<EntityHistory> Since(DateTime date, EntityHistoryEventType? eventType);
} }
public class HistoryRepository : BasicRepository<History>, IHistoryRepository public class HistoryRepository : BasicRepository<EntityHistory>, IHistoryRepository
{ {
public HistoryRepository(IMainDatabase database, IEventAggregator eventAggregator) public HistoryRepository(IMainDatabase database, IEventAggregator eventAggregator)
: base(database, eventAggregator) : base(database, eventAggregator)
{ {
} }
public History MostRecentForBook(int bookId) public EntityHistory MostRecentForBook(int bookId)
{ {
return Query(h => h.BookId == bookId) return Query(h => h.BookId == bookId)
.OrderByDescending(h => h.Date) .OrderByDescending(h => h.Date)
.FirstOrDefault(); .FirstOrDefault();
} }
public History MostRecentForDownloadId(string downloadId) public EntityHistory MostRecentForDownloadId(string downloadId)
{ {
return Query(h => h.DownloadId == downloadId) return Query(h => h.DownloadId == downloadId)
.OrderByDescending(h => h.Date) .OrderByDescending(h => h.Date)
.FirstOrDefault(); .FirstOrDefault();
} }
public List<History> FindByDownloadId(string downloadId) public List<EntityHistory> FindByDownloadId(string downloadId)
{ {
return _database.QueryJoined<History, Author, Book>( return _database.QueryJoined<EntityHistory, Author, Book>(
Builder() Builder()
.Join<History, Author>((h, a) => h.AuthorId == a.Id) .Join<EntityHistory, Author>((h, a) => h.AuthorId == a.Id)
.Join<History, Book>((h, a) => h.BookId == a.Id) .Join<EntityHistory, Book>((h, a) => h.BookId == a.Id)
.Where<History>(h => h.DownloadId == downloadId), .Where<EntityHistory>(h => h.DownloadId == downloadId),
(history, author, book) => (history, author, book) =>
{ {
history.Author = author; history.Author = author;
@ -56,30 +56,30 @@ namespace NzbDrone.Core.History
}).ToList(); }).ToList();
} }
public List<History> GetByAuthor(int authorId, HistoryEventType? eventType) public List<EntityHistory> GetByAuthor(int authorId, EntityHistoryEventType? eventType)
{ {
var builder = Builder().Where<History>(h => h.AuthorId == authorId); var builder = Builder().Where<EntityHistory>(h => h.AuthorId == authorId);
if (eventType.HasValue) if (eventType.HasValue)
{ {
builder.Where<History>(h => h.EventType == eventType); builder.Where<EntityHistory>(h => h.EventType == eventType);
} }
return Query(builder).OrderByDescending(h => h.Date).ToList(); return Query(builder).OrderByDescending(h => h.Date).ToList();
} }
public List<History> GetByBook(int bookId, HistoryEventType? eventType) public List<EntityHistory> GetByBook(int bookId, EntityHistoryEventType? eventType)
{ {
var builder = Builder() var builder = Builder()
.Join<History, Book>((h, a) => h.BookId == a.Id) .Join<EntityHistory, Book>((h, a) => h.BookId == a.Id)
.Where<History>(h => h.BookId == bookId); .Where<EntityHistory>(h => h.BookId == bookId);
if (eventType.HasValue) if (eventType.HasValue)
{ {
builder.Where<History>(h => h.EventType == eventType); builder.Where<EntityHistory>(h => h.EventType == eventType);
} }
return _database.QueryJoined<History, Book>( return _database.QueryJoined<EntityHistory, Book>(
builder, builder,
(history, book) => (history, book) =>
{ {
@ -88,9 +88,9 @@ namespace NzbDrone.Core.History
}).OrderByDescending(h => h.Date).ToList(); }).OrderByDescending(h => h.Date).ToList();
} }
public List<History> FindDownloadHistory(int idAuthorId, QualityModel quality) public List<EntityHistory> FindDownloadHistory(int idAuthorId, QualityModel quality)
{ {
var allowed = new[] { HistoryEventType.Grabbed, HistoryEventType.DownloadFailed, HistoryEventType.BookFileImported }; var allowed = new[] { EntityHistoryEventType.Grabbed, EntityHistoryEventType.DownloadFailed, EntityHistoryEventType.BookFileImported };
return Query(h => h.AuthorId == idAuthorId && return Query(h => h.AuthorId == idAuthorId &&
h.Quality == quality && h.Quality == quality &&
@ -103,12 +103,12 @@ namespace NzbDrone.Core.History
} }
protected override SqlBuilder PagedBuilder() => new SqlBuilder() protected override SqlBuilder PagedBuilder() => new SqlBuilder()
.Join<History, Author>((h, a) => h.AuthorId == a.Id) .Join<EntityHistory, Author>((h, a) => h.AuthorId == a.Id)
.Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id) .Join<Author, AuthorMetadata>((l, r) => l.AuthorMetadataId == r.Id)
.Join<History, Book>((h, a) => h.BookId == a.Id); .Join<EntityHistory, Book>((h, a) => h.BookId == a.Id);
protected override IEnumerable<History> PagedQuery(SqlBuilder builder) => protected override IEnumerable<EntityHistory> PagedQuery(SqlBuilder builder) =>
_database.QueryJoined<History, Author, AuthorMetadata, Book>(builder, (history, author, metadata, book) => _database.QueryJoined<EntityHistory, Author, AuthorMetadata, Book>(builder, (history, author, metadata, book) =>
{ {
author.Metadata = metadata; author.Metadata = metadata;
history.Author = author; history.Author = author;
@ -116,13 +116,13 @@ namespace NzbDrone.Core.History
return history; return history;
}); });
public List<History> Since(DateTime date, HistoryEventType? eventType) public List<EntityHistory> Since(DateTime date, EntityHistoryEventType? eventType)
{ {
var builder = Builder().Where<History>(x => x.Date >= date); var builder = Builder().Where<EntityHistory>(x => x.Date >= date);
if (eventType.HasValue) if (eventType.HasValue)
{ {
builder.Where<History>(h => h.EventType == eventType); builder.Where<EntityHistory>(h => h.EventType == eventType);
} }
return Query(builder).OrderBy(h => h.Date).ToList(); return Query(builder).OrderBy(h => h.Date).ToList();

@ -18,16 +18,17 @@ namespace NzbDrone.Core.History
{ {
public interface IHistoryService public interface IHistoryService
{ {
PagingSpec<History> Paged(PagingSpec<History> pagingSpec); PagingSpec<EntityHistory> Paged(PagingSpec<EntityHistory> pagingSpec);
History MostRecentForBook(int bookId); EntityHistory MostRecentForBook(int bookId);
History MostRecentForDownloadId(string downloadId); EntityHistory MostRecentForDownloadId(string downloadId);
History Get(int historyId); EntityHistory Get(int historyId);
List<History> GetByAuthor(int authorId, HistoryEventType? eventType); List<EntityHistory> GetByAuthor(int authorId, EntityHistoryEventType? eventType);
List<History> GetByBook(int bookId, HistoryEventType? eventType); List<EntityHistory> GetByBook(int bookId, EntityHistoryEventType? eventType);
List<History> Find(string downloadId, HistoryEventType eventType); List<EntityHistory> Find(string downloadId, EntityHistoryEventType eventType);
List<History> FindByDownloadId(string downloadId); List<EntityHistory> FindByDownloadId(string downloadId);
List<History> Since(DateTime date, HistoryEventType? eventType); string FindDownloadId(TrackImportedEvent trackedDownload);
void UpdateMany(IList<History> items); List<EntityHistory> Since(DateTime date, EntityHistoryEventType? eventType);
void UpdateMany(IList<EntityHistory> items);
} }
public class HistoryService : IHistoryService, public class HistoryService : IHistoryService,
@ -35,7 +36,6 @@ namespace NzbDrone.Core.History
IHandle<BookImportIncompleteEvent>, IHandle<BookImportIncompleteEvent>,
IHandle<TrackImportedEvent>, IHandle<TrackImportedEvent>,
IHandle<DownloadFailedEvent>, IHandle<DownloadFailedEvent>,
IHandle<DownloadCompletedEvent>,
IHandle<BookFileDeletedEvent>, IHandle<BookFileDeletedEvent>,
IHandle<BookFileRenamedEvent>, IHandle<BookFileRenamedEvent>,
IHandle<BookFileRetaggedEvent>, IHandle<BookFileRetaggedEvent>,
@ -51,62 +51,61 @@ namespace NzbDrone.Core.History
_logger = logger; _logger = logger;
} }
public PagingSpec<History> Paged(PagingSpec<History> pagingSpec) public PagingSpec<EntityHistory> Paged(PagingSpec<EntityHistory> pagingSpec)
{ {
return _historyRepository.GetPaged(pagingSpec); return _historyRepository.GetPaged(pagingSpec);
} }
public History MostRecentForBook(int bookId) public EntityHistory MostRecentForBook(int bookId)
{ {
return _historyRepository.MostRecentForBook(bookId); return _historyRepository.MostRecentForBook(bookId);
} }
public History MostRecentForDownloadId(string downloadId) public EntityHistory MostRecentForDownloadId(string downloadId)
{ {
return _historyRepository.MostRecentForDownloadId(downloadId); return _historyRepository.MostRecentForDownloadId(downloadId);
} }
public History Get(int historyId) public EntityHistory Get(int historyId)
{ {
return _historyRepository.Get(historyId); return _historyRepository.Get(historyId);
} }
public List<History> GetByAuthor(int authorId, HistoryEventType? eventType) public List<EntityHistory> GetByAuthor(int authorId, EntityHistoryEventType? eventType)
{ {
return _historyRepository.GetByAuthor(authorId, eventType); return _historyRepository.GetByAuthor(authorId, eventType);
} }
public List<History> GetByBook(int bookId, HistoryEventType? eventType) public List<EntityHistory> GetByBook(int bookId, EntityHistoryEventType? eventType)
{ {
return _historyRepository.GetByBook(bookId, eventType); return _historyRepository.GetByBook(bookId, eventType);
} }
public List<History> Find(string downloadId, HistoryEventType eventType) public List<EntityHistory> Find(string downloadId, EntityHistoryEventType eventType)
{ {
return _historyRepository.FindByDownloadId(downloadId).Where(c => c.EventType == eventType).ToList(); return _historyRepository.FindByDownloadId(downloadId).Where(c => c.EventType == eventType).ToList();
} }
public List<History> FindByDownloadId(string downloadId) public List<EntityHistory> FindByDownloadId(string downloadId)
{ {
return _historyRepository.FindByDownloadId(downloadId); return _historyRepository.FindByDownloadId(downloadId);
} }
private string FindDownloadId(TrackImportedEvent trackedDownload) public string FindDownloadId(TrackImportedEvent trackedDownload)
{ {
_logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedBook.Path); _logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedBook.Path);
var bookIds = new List<int> { trackedDownload.BookInfo.Book.Id }; var bookIds = new List<int> { trackedDownload.BookInfo.Book.Id };
var allHistory = _historyRepository.FindDownloadHistory(trackedDownload.BookInfo.Author.Id, trackedDownload.ImportedBook.Quality); var allHistory = _historyRepository.FindDownloadHistory(trackedDownload.BookInfo.Author.Id, trackedDownload.ImportedBook.Quality);
//Find download related items for these episdoes //Find download related items for these episodes
var booksHistory = allHistory.Where(h => bookIds.Contains(h.BookId)).ToList(); var booksHistory = allHistory.Where(h => bookIds.Contains(h.BookId)).ToList();
var processedDownloadId = booksHistory var processedDownloadId = booksHistory
.Where(c => c.EventType != HistoryEventType.Grabbed && c.DownloadId != null) .Where(c => c.EventType != EntityHistoryEventType.Grabbed && c.DownloadId != null)
.Select(c => c.DownloadId); .Select(c => c.DownloadId);
var stillDownloading = booksHistory.Where(c => c.EventType == HistoryEventType.Grabbed && !processedDownloadId.Contains(c.DownloadId)).ToList(); var stillDownloading = booksHistory.Where(c => c.EventType == EntityHistoryEventType.Grabbed && !processedDownloadId.Contains(c.DownloadId)).ToList();
string downloadId = null; string downloadId = null;
@ -138,9 +137,9 @@ namespace NzbDrone.Core.History
{ {
foreach (var book in message.Book.Books) foreach (var book in message.Book.Books)
{ {
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.Grabbed, EventType = EntityHistoryEventType.Grabbed,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.Book.ParsedBookInfo.Quality, Quality = message.Book.ParsedBookInfo.Quality,
SourceTitle = message.Book.Release.Title, SourceTitle = message.Book.Release.Title,
@ -157,6 +156,7 @@ namespace NzbDrone.Core.History
history.Data.Add("AgeMinutes", message.Book.Release.AgeMinutes.ToString()); history.Data.Add("AgeMinutes", message.Book.Release.AgeMinutes.ToString());
history.Data.Add("PublishedDate", message.Book.Release.PublishDate.ToString("s") + "Z"); history.Data.Add("PublishedDate", message.Book.Release.PublishDate.ToString("s") + "Z");
history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("DownloadClientName", message.DownloadClientName);
history.Data.Add("Size", message.Book.Release.Size.ToString()); history.Data.Add("Size", message.Book.Release.Size.ToString());
history.Data.Add("DownloadUrl", message.Book.Release.DownloadUrl); history.Data.Add("DownloadUrl", message.Book.Release.DownloadUrl);
history.Data.Add("Guid", message.Book.Release.Guid); history.Data.Add("Guid", message.Book.Release.Guid);
@ -188,9 +188,9 @@ namespace NzbDrone.Core.History
foreach (var book in message.TrackedDownload.RemoteBook.Books) foreach (var book in message.TrackedDownload.RemoteBook.Books)
{ {
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.BookImportIncomplete, EventType = EntityHistoryEventType.BookImportIncomplete,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.TrackedDownload.RemoteBook.ParsedBookInfo?.Quality ?? new QualityModel(), Quality = message.TrackedDownload.RemoteBook.ParsedBookInfo?.Quality ?? new QualityModel(),
SourceTitle = message.TrackedDownload.DownloadItem.Title, SourceTitle = message.TrackedDownload.DownloadItem.Title,
@ -218,9 +218,9 @@ namespace NzbDrone.Core.History
downloadId = FindDownloadId(message); downloadId = FindDownloadId(message);
} }
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.BookFileImported, EventType = EntityHistoryEventType.BookFileImported,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.BookInfo.Quality, Quality = message.BookInfo.Quality,
SourceTitle = message.ImportedBook.SceneName ?? Path.GetFileNameWithoutExtension(message.BookInfo.Path), SourceTitle = message.ImportedBook.SceneName ?? Path.GetFileNameWithoutExtension(message.BookInfo.Path),
@ -233,7 +233,8 @@ namespace NzbDrone.Core.History
//history.Data.Add("FileId", message.ImportedEpisode.Id.ToString()); //history.Data.Add("FileId", message.ImportedEpisode.Id.ToString());
history.Data.Add("DroppedPath", message.BookInfo.Path); history.Data.Add("DroppedPath", message.BookInfo.Path);
history.Data.Add("ImportedPath", message.ImportedBook.Path); history.Data.Add("ImportedPath", message.ImportedBook.Path);
history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClient", message.DownloadClientInfo?.Type);
history.Data.Add("DownloadClientName", message.DownloadClientInfo?.Name);
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
@ -242,9 +243,9 @@ namespace NzbDrone.Core.History
{ {
foreach (var bookId in message.BookIds) foreach (var bookId in message.BookIds)
{ {
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.DownloadFailed, EventType = EntityHistoryEventType.DownloadFailed,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.Quality, Quality = message.Quality,
SourceTitle = message.SourceTitle, SourceTitle = message.SourceTitle,
@ -254,31 +255,13 @@ namespace NzbDrone.Core.History
}; };
history.Data.Add("DownloadClient", message.DownloadClient); history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("DownloadClientName", message.TrackedDownload?.DownloadItem.DownloadClientInfo.Name);
history.Data.Add("Message", message.Message); history.Data.Add("Message", message.Message);
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
} }
public void Handle(DownloadCompletedEvent message)
{
foreach (var book in message.TrackedDownload.RemoteBook.Books)
{
var history = new History
{
EventType = HistoryEventType.DownloadImported,
Date = DateTime.UtcNow,
Quality = message.TrackedDownload.RemoteBook.ParsedBookInfo?.Quality ?? new QualityModel(),
SourceTitle = message.TrackedDownload.DownloadItem.Title,
AuthorId = book.AuthorId,
BookId = book.Id,
DownloadId = message.TrackedDownload.DownloadItem.DownloadId
};
_historyRepository.Insert(history);
}
}
public void Handle(BookFileDeletedEvent message) public void Handle(BookFileDeletedEvent message)
{ {
if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes) if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes)
@ -292,9 +275,9 @@ namespace NzbDrone.Core.History
return; return;
} }
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.BookFileDeleted, EventType = EntityHistoryEventType.BookFileDeleted,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.BookFile.Quality, Quality = message.BookFile.Quality,
SourceTitle = message.BookFile.Path, SourceTitle = message.BookFile.Path,
@ -312,9 +295,9 @@ namespace NzbDrone.Core.History
var sourcePath = message.OriginalPath; var sourcePath = message.OriginalPath;
var path = message.BookFile.Path; var path = message.BookFile.Path;
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.BookFileRenamed, EventType = EntityHistoryEventType.BookFileRenamed,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.BookFile.Quality, Quality = message.BookFile.Quality,
SourceTitle = message.OriginalPath, SourceTitle = message.OriginalPath,
@ -332,9 +315,9 @@ namespace NzbDrone.Core.History
{ {
var path = message.BookFile.Path; var path = message.BookFile.Path;
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.BookFileRetagged, EventType = EntityHistoryEventType.BookFileRetagged,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.BookFile.Quality, Quality = message.BookFile.Quality,
SourceTitle = path, SourceTitle = path,
@ -360,12 +343,12 @@ namespace NzbDrone.Core.History
public void Handle(DownloadIgnoredEvent message) public void Handle(DownloadIgnoredEvent message)
{ {
var historyToAdd = new List<History>(); var historyToAdd = new List<EntityHistory>();
foreach (var bookId in message.BookIds) foreach (var bookId in message.BookIds)
{ {
var history = new History var history = new EntityHistory
{ {
EventType = HistoryEventType.DownloadIgnored, EventType = EntityHistoryEventType.DownloadIgnored,
Date = DateTime.UtcNow, Date = DateTime.UtcNow,
Quality = message.Quality, Quality = message.Quality,
SourceTitle = message.SourceTitle, SourceTitle = message.SourceTitle,
@ -383,12 +366,12 @@ namespace NzbDrone.Core.History
_historyRepository.InsertMany(historyToAdd); _historyRepository.InsertMany(historyToAdd);
} }
public List<History> Since(DateTime date, HistoryEventType? eventType) public List<EntityHistory> Since(DateTime date, EntityHistoryEventType? eventType)
{ {
return _historyRepository.Since(date, eventType); return _historyRepository.Since(date, eventType);
} }
public void UpdateMany(IList<History> items) public void UpdateMany(IList<EntityHistory> items)
{ {
_historyRepository.UpdateMany(items); _historyRepository.UpdateMany(items);
} }

@ -39,8 +39,8 @@ namespace NzbDrone.Core.MediaFiles.BookImport.Specifications
} }
var bookHistory = _historyService.GetByBook(bookRelease.BookId, null); var bookHistory = _historyService.GetByBook(bookRelease.BookId, null);
var lastImported = bookHistory.FirstOrDefault(h => h.EventType == HistoryEventType.DownloadImported); var lastImported = bookHistory.FirstOrDefault(h => h.EventType == EntityHistoryEventType.BookFileImported);
var lastGrabbed = bookHistory.FirstOrDefault(h => h.EventType == HistoryEventType.Grabbed); var lastGrabbed = bookHistory.FirstOrDefault(h => h.EventType == EntityHistoryEventType.Grabbed);
if (lastImported == null) if (lastImported == null)
{ {

@ -12,7 +12,7 @@ namespace NzbDrone.Core.MediaFiles.Events
public List<BookFile> ImportedBooks { get; private set; } public List<BookFile> ImportedBooks { get; private set; }
public List<BookFile> OldFiles { get; private set; } public List<BookFile> OldFiles { get; private set; }
public bool NewDownload { get; private set; } public bool NewDownload { get; private set; }
public string DownloadClient { get; private set; } public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
public string DownloadId { get; private set; } public string DownloadId { get; private set; }
public BookImportedEvent(Author author, Book book, List<BookFile> importedBooks, List<BookFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem) public BookImportedEvent(Author author, Book book, List<BookFile> importedBooks, List<BookFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
@ -25,7 +25,7 @@ namespace NzbDrone.Core.MediaFiles.Events
if (downloadClientItem != null) if (downloadClientItem != null)
{ {
DownloadClient = downloadClientItem.DownloadClientInfo.Name; DownloadClientInfo = downloadClientItem.DownloadClientInfo;
DownloadId = downloadClientItem.DownloadId; DownloadId = downloadClientItem.DownloadId;
} }
} }

@ -10,7 +10,7 @@ namespace NzbDrone.Core.MediaFiles.Events
public Exception Exception { get; set; } public Exception Exception { get; set; }
public LocalBook BookInfo { get; } public LocalBook BookInfo { get; }
public bool NewDownload { get; } public bool NewDownload { get; }
public string DownloadClient { get; } public DownloadClientItemClientInfo DownloadClientInfo { get; }
public string DownloadId { get; } public string DownloadId { get; }
public TrackImportFailedEvent(Exception exception, LocalBook bookInfo, bool newDownload, DownloadClientItem downloadClientItem) public TrackImportFailedEvent(Exception exception, LocalBook bookInfo, bool newDownload, DownloadClientItem downloadClientItem)
@ -21,7 +21,7 @@ namespace NzbDrone.Core.MediaFiles.Events
if (downloadClientItem != null) if (downloadClientItem != null)
{ {
DownloadClient = downloadClientItem.DownloadClientInfo.Name; DownloadClientInfo = downloadClientItem.DownloadClientInfo;
DownloadId = downloadClientItem.DownloadId; DownloadId = downloadClientItem.DownloadId;
} }
} }

@ -11,7 +11,7 @@ namespace NzbDrone.Core.MediaFiles.Events
public BookFile ImportedBook { get; private set; } public BookFile ImportedBook { get; private set; }
public List<BookFile> OldFiles { get; private set; } public List<BookFile> OldFiles { get; private set; }
public bool NewDownload { get; private set; } public bool NewDownload { get; private set; }
public string DownloadClient { get; private set; } public DownloadClientItemClientInfo DownloadClientInfo { get; set; }
public string DownloadId { get; private set; } public string DownloadId { get; private set; }
public TrackImportedEvent(LocalBook bookInfo, BookFile importedBook, List<BookFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem) public TrackImportedEvent(LocalBook bookInfo, BookFile importedBook, List<BookFile> oldFiles, bool newDownload, DownloadClientItem downloadClientItem)
@ -23,7 +23,7 @@ namespace NzbDrone.Core.MediaFiles.Events
if (downloadClientItem != null) if (downloadClientItem != null)
{ {
DownloadClient = downloadClientItem.DownloadClientInfo.Name; DownloadClientInfo = downloadClientItem.DownloadClientInfo;
DownloadId = downloadClientItem.DownloadId; DownloadId = downloadClientItem.DownloadId;
} }
} }

@ -152,7 +152,7 @@ namespace NzbDrone.Core.Notifications
Message = GetBookDownloadMessage(message.Author, message.Book, message.ImportedBooks), Message = GetBookDownloadMessage(message.Author, message.Book, message.ImportedBooks),
Author = message.Author, Author = message.Author,
Book = message.Book, Book = message.Book,
DownloadClient = message.DownloadClient, DownloadClient = message.DownloadClientInfo?.Name,
DownloadId = message.DownloadId, DownloadId = message.DownloadId,
BookFiles = message.ImportedBooks, BookFiles = message.ImportedBooks,
OldFiles = message.OldFiles, OldFiles = message.OldFiles,

@ -63,7 +63,7 @@ namespace NzbDrone.Core.Queue
private Queue MapQueueItem(TrackedDownload trackedDownload, Book book) private Queue MapQueueItem(TrackedDownload trackedDownload, Book book)
{ {
bool downloadForced = false; bool downloadForced = false;
var history = _historyService.Find(trackedDownload.DownloadItem.DownloadId, HistoryEventType.Grabbed).FirstOrDefault(); var history = _historyService.Find(trackedDownload.DownloadItem.DownloadId, EntityHistoryEventType.Grabbed).FirstOrDefault();
if (history != null && history.Data.ContainsKey("downloadForced")) if (history != null && history.Data.ContainsKey("downloadForced"))
{ {
downloadForced = bool.Parse(history.Data["downloadForced"]); downloadForced = bool.Parse(history.Data["downloadForced"]);

@ -29,7 +29,7 @@ namespace Readarr.Api.V1.History
_failedDownloadService = failedDownloadService; _failedDownloadService = failedDownloadService;
} }
protected HistoryResource MapToResource(NzbDrone.Core.History.History model, bool includeAuthor, bool includeBook) protected HistoryResource MapToResource(EntityHistory model, bool includeAuthor, bool includeBook)
{ {
var resource = model.ToResource(); var resource = model.ToResource();
@ -55,7 +55,7 @@ namespace Readarr.Api.V1.History
public PagingResource<HistoryResource> GetHistory(bool includeAuthor = false, bool includeBook = false) public PagingResource<HistoryResource> GetHistory(bool includeAuthor = false, bool includeBook = false)
{ {
var pagingResource = Request.ReadPagingResourceFromRequest<HistoryResource>(); var pagingResource = Request.ReadPagingResourceFromRequest<HistoryResource>();
var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, NzbDrone.Core.History.History>("date", SortDirection.Descending); var pagingSpec = pagingResource.MapToPagingSpec<HistoryResource, EntityHistory>("date", SortDirection.Descending);
var eventTypeFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "eventType"); var eventTypeFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "eventType");
var bookIdFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "bookId"); var bookIdFilter = pagingResource.Filters.FirstOrDefault(f => f.Key == "bookId");
@ -63,7 +63,7 @@ namespace Readarr.Api.V1.History
if (eventTypeFilter != null) if (eventTypeFilter != null)
{ {
var filterValue = (HistoryEventType)Convert.ToInt32(eventTypeFilter.Value); var filterValue = (EntityHistoryEventType)Convert.ToInt32(eventTypeFilter.Value);
pagingSpec.FilterExpressions.Add(v => v.EventType == filterValue); pagingSpec.FilterExpressions.Add(v => v.EventType == filterValue);
} }
@ -83,13 +83,13 @@ namespace Readarr.Api.V1.History
} }
[HttpGet("since")] [HttpGet("since")]
public List<HistoryResource> GetHistorySince(DateTime date, HistoryEventType? eventType = null, bool includeAuthor = false, bool includeBook = false) public List<HistoryResource> GetHistorySince(DateTime date, EntityHistoryEventType? eventType = null, bool includeAuthor = false, bool includeBook = false)
{ {
return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeAuthor, includeBook)).ToList(); return _historyService.Since(date, eventType).Select(h => MapToResource(h, includeAuthor, includeBook)).ToList();
} }
[HttpGet("author")] [HttpGet("author")]
public List<HistoryResource> GetAuthorHistory(int authorId, int? bookId = null, HistoryEventType? eventType = null, bool includeAuthor = false, bool includeBook = false) public List<HistoryResource> GetAuthorHistory(int authorId, int? bookId = null, EntityHistoryEventType? eventType = null, bool includeAuthor = false, bool includeBook = false)
{ {
if (bookId.HasValue) if (bookId.HasValue)
{ {

@ -18,7 +18,7 @@ namespace Readarr.Api.V1.History
public DateTime Date { get; set; } public DateTime Date { get; set; }
public string DownloadId { get; set; } public string DownloadId { get; set; }
public HistoryEventType EventType { get; set; } public EntityHistoryEventType EventType { get; set; }
public Dictionary<string, string> Data { get; set; } public Dictionary<string, string> Data { get; set; }
@ -28,7 +28,7 @@ namespace Readarr.Api.V1.History
public static class HistoryResourceMapper public static class HistoryResourceMapper
{ {
public static HistoryResource ToResource(this NzbDrone.Core.History.History model) public static HistoryResource ToResource(this EntityHistory model)
{ {
if (model == null) if (model == null)
{ {

Loading…
Cancel
Save