New: Search for previously aired episodes that were just added to the database

pull/2/head
Mark McDowall 10 years ago
parent 709f96920d
commit 3f8366a190

@ -0,0 +1,42 @@
using System;
namespace NzbDrone.Common.Extensions
{
public static class DateTimeExtensions
{
public static bool InNextDays(this DateTime dateTime, int days)
{
return InNext(dateTime, new TimeSpan(days, 0, 0, 0));
}
public static bool InLastDays(this DateTime dateTime, int days)
{
return InLast(dateTime, new TimeSpan(days, 0, 0, 0));
}
public static bool InNext(this DateTime dateTime, TimeSpan timeSpan)
{
return dateTime >= DateTime.UtcNow && dateTime <= DateTime.UtcNow.Add(timeSpan);
}
public static bool InLast(this DateTime dateTime, TimeSpan timeSpan)
{
return dateTime >= DateTime.UtcNow.Add(-timeSpan) && dateTime <= DateTime.UtcNow;
}
public static bool Before(this DateTime dateTime, DateTime beforeDateTime)
{
return dateTime <= beforeDateTime;
}
public static bool After(this DateTime dateTime, DateTime afterDateTime)
{
return dateTime >= afterDateTime;
}
public static bool Between(this DateTime dateTime, DateTime afterDateTime, DateTime beforeDateTime)
{
return dateTime >= afterDateTime && dateTime <= beforeDateTime;
}
}
}

@ -128,6 +128,7 @@
<Compile Include="Expansive\TreeNode.cs" />
<Compile Include="Expansive\TreeNodeList.cs" />
<Compile Include="Extensions\Base64Extentions.cs" />
<Compile Include="Extensions\DateTimeExtensions.cs" />
<Compile Include="Extensions\Int64Extensions.cs" />
<Compile Include="Extensions\StreamExtensions.cs" />
<Compile Include="Extensions\XmlExtentions.cs" />

@ -3,7 +3,6 @@ using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Zip;
namespace NzbDrone.Common
{

@ -0,0 +1,122 @@
using System;
using System.Collections.Generic;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Download;
using NzbDrone.Core.IndexerSearch;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.Test.IndexerSearchTests
{
[TestFixture]
public class EpisodeInfoRefreshedSearchFixture : CoreTest<EpisodeSearchService>
{
private Series _series;
private IList<Episode> _added;
private IList<Episode> _updated;
[SetUp]
public void Setup()
{
_series = Builder<Series>.CreateNew()
.With(s => s.Added = DateTime.UtcNow.AddDays(-7))
.Build();
_added = new List<Episode>();
_updated = new List<Episode>();
}
private void GivenUpdated()
{
_updated.Add(Builder<Episode>.CreateNew().Build());
}
[Test]
public void should_not_search_if_no_episodes_were_upgraded()
{
_added.Add(new Episode());
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
VerifyNoSearch();
}
[Test]
public void should_not_search_if_series_was_added_within_the_last_day()
{
GivenUpdated();
_series.Added = DateTime.UtcNow;
_added.Add(new Episode());
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
VerifyNoSearch();
}
[Test]
public void should_not_search_if_no_episodes_were_added()
{
GivenUpdated();
_updated.Add(new Episode());
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
VerifyNoSearch();
}
[Test]
public void should_not_search_if_air_date_doesnt_have_a_value()
{
GivenUpdated();
_added.Add(new Episode());
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
VerifyNoSearch();
}
[Test]
public void should_not_search_if_episodes_air_in_the_future()
{
GivenUpdated();
_added.Add(new Episode { AirDateUtc = DateTime.UtcNow.AddDays(7) });
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
VerifyNoSearch();
}
[Test]
public void should_search_for_a_newly_added_episode()
{
GivenUpdated();
_added.Add(new Episode { AirDateUtc = DateTime.UtcNow });
Mocker.GetMock<IProcessDownloadDecisions>()
.Setup(s => s.ProcessDecisions(It.IsAny<List<DownloadDecision>>()))
.Returns(new ProcessedDecisions(new List<DownloadDecision>(), new List<DownloadDecision>()));
Subject.Handle(new EpisodeInfoRefreshedEvent(_series, _added, _updated));
Mocker.GetMock<ISearchForNzb>()
.Verify(v => v.EpisodeSearch(It.IsAny<Episode>()), Times.Once());
}
private void VerifyNoSearch()
{
Mocker.GetMock<ISearchForNzb>()
.Verify(v => v.EpisodeSearch(It.IsAny<Episode>()), Times.Never());
}
}
}

@ -179,6 +179,7 @@
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedMetadataFilesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\CleanupOrphanedPendingReleasesFixture.cs" />
<Compile Include="Housekeeping\Housekeepers\FixFutureRunScheduledTasksFixture.cs" />
<Compile Include="IndexerSearchTests\EpisodeInfoRefreshedSearchFixture.cs" />
<Compile Include="IndexerSearchTests\SeriesSearchServiceFixture.cs" />
<Compile Include="IndexerSearchTests\NzbSearchServiceFixture.cs" />
<Compile Include="IndexerSearchTests\SearchDefinitionFixture.cs" />

@ -3,22 +3,27 @@ using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common;
using NzbDrone.Common.Extensions;
using NzbDrone.Common.Instrumentation.Extensions;
using NzbDrone.Core.Datastore;
using NzbDrone.Core.Download;
using NzbDrone.Core.Messaging.Commands;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Queue;
using NzbDrone.Core.Tv;
using NzbDrone.Core.Tv.Events;
namespace NzbDrone.Core.IndexerSearch
{
public interface IEpisodeSearchService
{
void MissingEpisodesAiredAfter(DateTime dateTime, IEnumerable<Int32> grabbed
);
void MissingEpisodesAiredAfter(DateTime dateTime, IEnumerable<Int32> grabbed);
}
public class MissingEpisodeSearchService : IEpisodeSearchService, IExecute<EpisodeSearchCommand>, IExecute<MissingEpisodeSearchCommand>
public class EpisodeSearchService : IEpisodeSearchService,
IExecute<EpisodeSearchCommand>,
IExecute<MissingEpisodeSearchCommand>,
IHandle<EpisodeInfoRefreshedEvent>
{
private readonly ISearchForNzb _nzbSearchService;
private readonly IProcessDownloadDecisions _processDownloadDecisions;
@ -26,7 +31,7 @@ namespace NzbDrone.Core.IndexerSearch
private readonly IQueueService _queueService;
private readonly Logger _logger;
public MissingEpisodeSearchService(ISearchForNzb nzbSearchService,
public EpisodeSearchService(ISearchForNzb nzbSearchService,
IProcessDownloadDecisions processDownloadDecisions,
IEpisodeService episodeService,
IQueueService queueService,
@ -105,5 +110,42 @@ namespace NzbDrone.Core.IndexerSearch
_logger.ProgressInfo("Completed missing search for {0} episodes. {1} reports downloaded.", missing.Count, downloadedCount);
}
public void Handle(EpisodeInfoRefreshedEvent message)
{
if (message.Updated.Empty() || message.Series.Added.InLastDays(1))
{
_logger.Debug("Appears to be a new series, skipping search.");
return;
}
if (message.Added.Empty())
{
_logger.Debug("No new episodes, skipping search");
return;
}
if (message.Added.None(a => a.AirDateUtc.HasValue))
{
_logger.Debug("No new episodes have an air date");
return;
}
var previouslyAired = message.Added.Where(a => a.AirDateUtc.HasValue && a.AirDateUtc.Value.Before(DateTime.UtcNow.AddDays(1))).ToList();
if (previouslyAired.Empty())
{
_logger.Debug("Newly added episodes all air in the future");
return;
}
foreach (var episode in previouslyAired)
{
var decisions = _nzbSearchService.EpisodeSearch(episode);
var processed = _processDownloadDecisions.ProcessDecisions(decisions);
_logger.ProgressInfo("Episode search completed. {0} reports downloaded.", processed.Grabbed.Count);
}
}
}
}

@ -796,9 +796,7 @@
<SubType>Code</SubType>
</Compile>
<Compile Include="Tv\EpisodeService.cs" />
<Compile Include="Tv\Events\EpisodeInfoAddedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoDeletedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoUpdatedEvent.cs" />
<Compile Include="Tv\Events\EpisodeInfoRefreshedEvent.cs" />
<Compile Include="Tv\Events\SeriesAddedEvent.cs" />
<Compile Include="Tv\Events\SeriesDeletedEvent.cs" />
<Compile Include="Tv\Events\SeriesEditedEvent.cs" />

@ -1,18 +0,0 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoAddedEvent : IEvent
{
public Series Series { get; private set; }
public ReadOnlyCollection<Episode> Episodes { get; private set; }
public EpisodeInfoAddedEvent(IList<Episode> episodes, Series series)
{
Series = series;
Episodes = new ReadOnlyCollection<Episode>(episodes);
}
}
}

@ -1,16 +0,0 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoDeletedEvent : IEvent
{
public ReadOnlyCollection<Episode> Episodes { get; private set; }
public EpisodeInfoDeletedEvent(IList<Episode> episodes)
{
Episodes = new ReadOnlyCollection<Episode>(episodes);
}
}
}

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoRefreshedEvent : IEvent
{
public Series Series { get; set; }
public ReadOnlyCollection<Episode> Added { get; private set; }
public ReadOnlyCollection<Episode> Updated { get; private set; }
public EpisodeInfoRefreshedEvent(Series series, IList<Episode> added, IList<Episode> updated)
{
Series = series;
Added = new ReadOnlyCollection<Episode>(added);
Updated = new ReadOnlyCollection<Episode>(updated);
}
}
}

@ -1,16 +0,0 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using NzbDrone.Common.Messaging;
namespace NzbDrone.Core.Tv.Events
{
public class EpisodeInfoUpdatedEvent : IEvent
{
public ReadOnlyCollection<Episode> Episodes { get; private set; }
public EpisodeInfoUpdatedEvent(IList<Episode> episodes)
{
Episodes = new ReadOnlyCollection<Episode>(episodes);
}
}
}

@ -96,20 +96,7 @@ namespace NzbDrone.Core.Tv
_episodeService.UpdateMany(updateList);
_episodeService.InsertMany(newList);
if (newList.Any())
{
_eventAggregator.PublishEvent(new EpisodeInfoAddedEvent(newList, series));
}
if (updateList.Any())
{
_eventAggregator.PublishEvent(new EpisodeInfoUpdatedEvent(updateList));
}
if (existingEpisodes.Any())
{
_eventAggregator.PublishEvent(new EpisodeInfoDeletedEvent(updateList));
}
_eventAggregator.PublishEvent(new EpisodeInfoRefreshedEvent(series, newList, updateList));
if (failCount != 0)
{

Loading…
Cancel
Save