Fixed: Multiple History Issues (#2571)

pull/2587/head
Qstick 7 years ago committed by Leonardo Galli
parent 15fe7a844c
commit 9e7e8cff11

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
@ -72,7 +72,7 @@ namespace NzbDrone.Core.Test.DecisionEngineTests
private void GivenMostRecentForEpisode(int episodeId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType) private void GivenMostRecentForEpisode(int episodeId, string downloadId, QualityModel quality, DateTime date, HistoryEventType eventType)
{ {
Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForEpisode(episodeId)) Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForMovie(episodeId))
.Returns(new History.History { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType }); .Returns(new History.History { DownloadId = downloadId, Quality = quality, Date = date, EventType = eventType });
} }
@ -92,7 +92,7 @@ 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.MostRecentForEpisode(It.IsAny<int>())).Returns((History.History)null); Mocker.GetMock<IHistoryService>().Setup(s => s.MostRecentForMovie(It.IsAny<int>())).Returns((History.History)null);
_upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue(); _upgradeHistory.IsSatisfiedBy(_parseResultMulti, null).Accepted.Should().BeTrue();
} }

@ -1,4 +1,4 @@
using FizzWare.NBuilder; using FizzWare.NBuilder;
using FluentAssertions; using FluentAssertions;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Core.History; using NzbDrone.Core.History;
@ -32,13 +32,13 @@ namespace NzbDrone.Core.Test.HistoryTests
{ {
var historyBluray = Builder<History.History>.CreateNew() var historyBluray = Builder<History.History>.CreateNew()
.With(c => c.Quality = new QualityModel(Quality.Bluray1080p)) .With(c => c.Quality = new QualityModel(Quality.Bluray1080p))
.With(c => c.SeriesId = 12) .With(c => c.MovieId = 12)
.With(c => c.EventType = HistoryEventType.Grabbed) .With(c => c.EventType = HistoryEventType.Grabbed)
.BuildNew(); .BuildNew();
var historyDvd = Builder<History.History>.CreateNew() var historyDvd = Builder<History.History>.CreateNew()
.With(c => c.Quality = new QualityModel(Quality.DVD)) .With(c => c.Quality = new QualityModel(Quality.DVD))
.With(c => c.SeriesId = 12) .With(c => c.MovieId = 12)
.With(c => c.EventType = HistoryEventType.Grabbed) .With(c => c.EventType = HistoryEventType.Grabbed)
.BuildNew(); .BuildNew();

@ -1,4 +1,4 @@
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using FizzWare.NBuilder; using FizzWare.NBuilder;
using Moq; using Moq;
@ -68,23 +68,23 @@ namespace NzbDrone.Core.Test.HistoryTests
[Test] [Test]
public void should_use_file_name_for_source_title_if_scene_name_is_null() public void should_use_file_name_for_source_title_if_scene_name_is_null()
{ {
var series = Builder<Series>.CreateNew().Build(); // Test fails becuase Radarr is using movie.title in historyService with no fallback
var episodes = Builder<Episode>.CreateListOfSize(1).Build().ToList();
var episodeFile = Builder<EpisodeFile>.CreateNew() var movie = Builder<Movie>.CreateNew().Build();
var movieFile = Builder<MovieFile>.CreateNew()
.With(f => f.SceneName = null) .With(f => f.SceneName = null)
.Build(); .Build();
var localEpisode = new LocalEpisode var localMovie = new LocalMovie()
{ {
Series = series, Movie = movie,
Episodes = episodes, Path = @"C:\Test\Unsorted\Movie.2011.mkv"
Path = @"C:\Test\Unsorted\Series.s01e01.mkv"
}; };
Subject.Handle(new EpisodeImportedEvent(localEpisode, episodeFile, true, "sab", "abcd", true)); Subject.Handle(new MovieImportedEvent(localMovie, movieFile, true, "sab", "abcd", true));
Mocker.GetMock<IHistoryRepository>() Mocker.GetMock<IHistoryRepository>()
.Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localEpisode.Path)))); .Verify(v => v.Insert(It.Is<History.History>(h => h.SourceTitle == Path.GetFileNameWithoutExtension(localMovie.Path))));
} }
} }
} }

@ -80,54 +80,7 @@ namespace NzbDrone.Core.DecisionEngine.Specifications.RssSync
public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria) public virtual Decision IsSatisfiedBy(RemoteEpisode subject, SearchCriteriaBase searchCriteria)
{ {
if (searchCriteria != null) throw new NotImplementedException();
{
_logger.Debug("Skipping history check during search");
return Decision.Accept();
}
var cdhEnabled = _configService.EnableCompletedDownloadHandling;
_logger.Debug("Performing history status check on report");
foreach (var episode in subject.Episodes)
{
_logger.Debug("Checking current status of episode [{0}] in history", episode.Id);
var mostRecent = _historyService.MostRecentForEpisode(episode.Id);
if (mostRecent != null && mostRecent.EventType == HistoryEventType.Grabbed)
{
var recent = mostRecent.Date.After(DateTime.UtcNow.AddHours(-12));
var cutoffUnmet = _qualityUpgradableSpecification.CutoffNotMet(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality);
var upgradeable = _qualityUpgradableSpecification.IsUpgradable(subject.Series.Profile, mostRecent.Quality, subject.ParsedEpisodeInfo.Quality);
if (!recent && cdhEnabled)
{
continue;
}
if (!cutoffUnmet)
{
if (recent)
{
return Decision.Reject("Recent grab event in history already meets cutoff: {0}", mostRecent.Quality);
}
return Decision.Reject("CDH is disabled and grab event in history already meets cutoff: {0}", mostRecent.Quality);
}
if (!upgradeable)
{
if (recent)
{
return Decision.Reject("Recent grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
}
return Decision.Reject("CDH is disabled and grab event in history is of equal or higher quality: {0}", mostRecent.Quality);
}
}
}
return Decision.Accept();
} }
} }
} }

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Marr.Data.QGen; using Marr.Data.QGen;
@ -11,12 +11,10 @@ namespace NzbDrone.Core.History
{ {
public interface IHistoryRepository : IBasicRepository<History> public interface IHistoryRepository : IBasicRepository<History>
{ {
List<QualityModel> GetBestQualityInHistory(int episodeId); List<QualityModel> GetBestQualityInHistory(int movieId);
History MostRecentForEpisode(int episodeId);
History MostRecentForDownloadId(string downloadId); History MostRecentForDownloadId(string downloadId);
List<History> FindByDownloadId(string downloadId); List<History> FindByDownloadId(string downloadId);
List<History> FindDownloadHistory(int idSeriesId, QualityModel quality); List<History> FindDownloadHistory(int idMovieId, QualityModel quality);
void DeleteForSeries(int seriesId);
void DeleteForMovie(int movieId); void DeleteForMovie(int movieId);
History MostRecentForMovie(int movieId); History MostRecentForMovie(int movieId);
} }
@ -29,21 +27,13 @@ namespace NzbDrone.Core.History
{ {
} }
public List<QualityModel> GetBestQualityInHistory(int movieId)
public List<QualityModel> GetBestQualityInHistory(int episodeId)
{ {
var history = Query.Where(c => c.EpisodeId == episodeId); var history = Query.Where(c => c.MovieId == movieId);
return history.Select(h => h.Quality).ToList(); return history.Select(h => h.Quality).ToList();
} }
public History MostRecentForEpisode(int episodeId)
{
return Query.Where(h => h.EpisodeId == episodeId)
.OrderByDescending(h => h.Date)
.FirstOrDefault();
}
public History MostRecentForDownloadId(string downloadId) public History MostRecentForDownloadId(string downloadId)
{ {
return Query.Where(h => h.DownloadId == downloadId) return Query.Where(h => h.DownloadId == downloadId)
@ -56,10 +46,10 @@ namespace NzbDrone.Core.History
return Query.Where(h => h.DownloadId == downloadId); return Query.Where(h => h.DownloadId == downloadId);
} }
public List<History> FindDownloadHistory(int idSeriesId, QualityModel quality) public List<History> FindDownloadHistory(int idMovieId, QualityModel quality)
{ {
return Query.Where(h => return Query.Where(h =>
h.SeriesId == idSeriesId && h.MovieId == idMovieId &&
h.Quality == quality && h.Quality == quality &&
(h.EventType == HistoryEventType.Grabbed || (h.EventType == HistoryEventType.Grabbed ||
h.EventType == HistoryEventType.DownloadFailed || h.EventType == HistoryEventType.DownloadFailed ||
@ -67,11 +57,6 @@ namespace NzbDrone.Core.History
).ToList(); ).ToList();
} }
public void DeleteForSeries(int seriesId)
{
Delete(c => c.SeriesId == seriesId);
}
public void DeleteForMovie(int movieId) public void DeleteForMovie(int movieId)
{ {
Delete(c => c.MovieId == movieId); Delete(c => c.MovieId == movieId);

@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -21,7 +21,6 @@ namespace NzbDrone.Core.History
QualityModel GetBestQualityInHistory(Profile profile, int episodeId); QualityModel GetBestQualityInHistory(Profile profile, int episodeId);
PagingSpec<History> Paged(PagingSpec<History> pagingSpec); PagingSpec<History> Paged(PagingSpec<History> pagingSpec);
History MostRecentForMovie(int movieId); History MostRecentForMovie(int movieId);
History MostRecentForEpisode(int episodeId);
History MostRecentForDownloadId(string downloadId); History MostRecentForDownloadId(string downloadId);
History Get(int historyId); History Get(int historyId);
List<History> Find(string downloadId, HistoryEventType eventType); List<History> Find(string downloadId, HistoryEventType eventType);
@ -29,14 +28,11 @@ namespace NzbDrone.Core.History
} }
public class HistoryService : IHistoryService, public class HistoryService : IHistoryService,
IHandle<EpisodeGrabbedEvent>,
IHandle<MovieGrabbedEvent>, IHandle<MovieGrabbedEvent>,
IHandle<MovieImportedEvent>, IHandle<MovieImportedEvent>,
IHandle<EpisodeImportedEvent>,
IHandle<DownloadFailedEvent>, IHandle<DownloadFailedEvent>,
IHandle<EpisodeFileDeletedEvent>,
IHandle<MovieFileDeletedEvent>, IHandle<MovieFileDeletedEvent>,
IHandle<SeriesDeletedEvent> IHandle<MovieDeletedEvent>
{ {
private readonly IHistoryRepository _historyRepository; private readonly IHistoryRepository _historyRepository;
private readonly Logger _logger; private readonly Logger _logger;
@ -52,11 +48,6 @@ namespace NzbDrone.Core.History
return _historyRepository.GetPaged(pagingSpec); return _historyRepository.GetPaged(pagingSpec);
} }
public History MostRecentForEpisode(int episodeId)
{
return _historyRepository.MostRecentForEpisode(episodeId);
}
public History MostRecentForMovie(int movieId) public History MostRecentForMovie(int movieId)
{ {
return _historyRepository.MostRecentForMovie(movieId); return _historyRepository.MostRecentForMovie(movieId);
@ -205,10 +196,7 @@ namespace NzbDrone.Core.History
var movieId = trackedDownload.MovieInfo.Movie.Id; var movieId = trackedDownload.MovieInfo.Movie.Id;
var allHistory = _historyRepository.FindDownloadHistory(movieId, trackedDownload.ImportedMovie.Quality); var movieHistory = _historyRepository.FindDownloadHistory(movieId, trackedDownload.ImportedMovie.Quality);
//Find download related items for this movie
var movieHistory = allHistory.Where(h => movieId == h.MovieId).ToList();
var processedDownloadId = movieHistory var processedDownloadId = movieHistory
.Where(c => c.EventType != HistoryEventType.Grabbed && c.DownloadId != null) .Where(c => c.EventType != HistoryEventType.Grabbed && c.DownloadId != null)
@ -244,136 +232,6 @@ namespace NzbDrone.Core.History
return downloadId; return downloadId;
} }
private string FindDownloadId(EpisodeImportedEvent trackedDownload)
{
_logger.Debug("Trying to find downloadId for {0} from history", trackedDownload.ImportedEpisode.Path);
var episodeIds = trackedDownload.EpisodeInfo.Episodes.Select(c => c.Id).ToList();
var allHistory = _historyRepository.FindDownloadHistory(trackedDownload.EpisodeInfo.Series.Id, trackedDownload.ImportedEpisode.Quality);
//Find download related items for these episdoes
var episodesHistory = allHistory.Where(h => episodeIds.Contains(h.EpisodeId)).ToList();
var processedDownloadId = episodesHistory
.Where(c => c.EventType != HistoryEventType.Grabbed && c.DownloadId != null)
.Select(c => c.DownloadId);
var stillDownloading = episodesHistory.Where(c => c.EventType == HistoryEventType.Grabbed && !processedDownloadId.Contains(c.DownloadId)).ToList();
string downloadId = null;
if (stillDownloading.Any())
{
foreach (var matchingHistory in trackedDownload.EpisodeInfo.Episodes.Select(e => stillDownloading.Where(c => c.EpisodeId == e.Id).ToList()))
{
if (matchingHistory.Count != 1)
{
return null;
}
var newDownloadId = matchingHistory.Single().DownloadId;
if (downloadId == null || downloadId == newDownloadId)
{
downloadId = newDownloadId;
}
else
{
return null;
}
}
}
return downloadId;
}
public void Handle(EpisodeGrabbedEvent message)
{
foreach (var episode in message.Episode.Episodes)
{
var history = new History
{
EventType = HistoryEventType.Grabbed,
Date = DateTime.UtcNow,
Quality = message.Episode.ParsedEpisodeInfo.Quality,
SourceTitle = message.Episode.Release.Title,
SeriesId = episode.SeriesId,
EpisodeId = episode.Id,
DownloadId = message.DownloadId,
MovieId = 0
};
history.Data.Add("Indexer", message.Episode.Release.Indexer);
history.Data.Add("NzbInfoUrl", message.Episode.Release.InfoUrl);
history.Data.Add("ReleaseGroup", message.Episode.ParsedEpisodeInfo.ReleaseGroup);
history.Data.Add("Age", message.Episode.Release.Age.ToString());
history.Data.Add("AgeHours", message.Episode.Release.AgeHours.ToString());
history.Data.Add("AgeMinutes", message.Episode.Release.AgeMinutes.ToString());
history.Data.Add("PublishedDate", message.Episode.Release.PublishDate.ToString("s") + "Z");
history.Data.Add("DownloadClient", message.DownloadClient);
history.Data.Add("Size", message.Episode.Release.Size.ToString());
history.Data.Add("DownloadUrl", message.Episode.Release.DownloadUrl);
history.Data.Add("Guid", message.Episode.Release.Guid);
history.Data.Add("TvdbId", message.Episode.Release.TvdbId.ToString());
history.Data.Add("TvRageId", message.Episode.Release.TvRageId.ToString());
history.Data.Add("Protocol", ((int)message.Episode.Release.DownloadProtocol).ToString());
if (!message.Episode.ParsedEpisodeInfo.ReleaseHash.IsNullOrWhiteSpace())
{
history.Data.Add("ReleaseHash", message.Episode.ParsedEpisodeInfo.ReleaseHash);
}
var torrentRelease = message.Episode.Release as TorrentInfo;
if (torrentRelease != null)
{
history.Data.Add("TorrentInfoHash", torrentRelease.InfoHash);
}
_historyRepository.Insert(history);
}
}
public void Handle(EpisodeImportedEvent message)
{
if (!message.NewDownload)
{
return;
}
var downloadId = message.DownloadId;
if (downloadId.IsNullOrWhiteSpace())
{
downloadId = FindDownloadId(message);
}
foreach (var episode in message.EpisodeInfo.Episodes)
{
var history = new History
{
EventType = HistoryEventType.DownloadFolderImported,
Date = DateTime.UtcNow,
Quality = message.EpisodeInfo.Quality,
SourceTitle = message.ImportedEpisode.SceneName ?? Path.GetFileNameWithoutExtension(message.EpisodeInfo.Path),
SeriesId = message.ImportedEpisode.SeriesId,
EpisodeId = episode.Id,
DownloadId = downloadId,
MovieId = 0,
};
//Won't have a value since we publish this event before saving to DB.
//history.Data.Add("FileId", message.ImportedEpisode.Id.ToString());
history.Data.Add("DroppedPath", message.EpisodeInfo.Path);
history.Data.Add("ImportedPath", Path.Combine(message.EpisodeInfo.Series.Path, message.ImportedEpisode.RelativePath));
history.Data.Add("DownloadClient", message.DownloadClient);
_historyRepository.Insert(history);
}
}
public void Handle(DownloadFailedEvent message) public void Handle(DownloadFailedEvent message)
{ {
var history = new History var history = new History
@ -393,37 +251,5 @@ namespace NzbDrone.Core.History
_historyRepository.Insert(history); _historyRepository.Insert(history);
} }
public void Handle(EpisodeFileDeletedEvent message)
{
if (message.Reason == DeleteMediaFileReason.NoLinkedEpisodes)
{
_logger.Debug("Removing episode file from DB as part of cleanup routine, not creating history event.");
return;
}
foreach (var episode in message.EpisodeFile.Episodes.Value)
{
var history = new History
{
EventType = HistoryEventType.EpisodeFileDeleted,
Date = DateTime.UtcNow,
Quality = message.EpisodeFile.Quality,
SourceTitle = message.EpisodeFile.Path,
SeriesId = message.EpisodeFile.SeriesId,
EpisodeId = episode.Id,
MovieId = 0
};
history.Data.Add("Reason", message.Reason.ToString());
_historyRepository.Insert(history);
}
}
public void Handle(SeriesDeletedEvent message)
{
_historyRepository.DeleteForSeries(message.Series.Id);
}
} }
} }

@ -7,7 +7,7 @@
{{#if_eq eventType compare="grabbed"}}Grabbed{{/if_eq}} {{#if_eq eventType compare="grabbed"}}Grabbed{{/if_eq}}
{{#if_eq eventType compare="downloadFailed"}}Download Failed{{/if_eq}} {{#if_eq eventType compare="downloadFailed"}}Download Failed{{/if_eq}}
{{#if_eq eventType compare="downloadFolderImported"}}Movie Imported{{/if_eq}} {{#if_eq eventType compare="downloadFolderImported"}}Movie Imported{{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}}Movie File Deleted{{/if_eq}} {{#if_eq eventType compare="movieFileDeleted"}}Movie File Deleted{{/if_eq}}
</h3> </h3>
</div> </div>

@ -77,7 +77,7 @@
</dl> </dl>
{{/if_eq}} {{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}} {{#if_eq eventType compare="movieFileDeleted"}}
<dl class="dl-horizontal"> <dl class="dl-horizontal">
<dt>Path:</dt> <dt>Path:</dt>

Loading…
Cancel
Save