From 6dc0a8800435afb50e7974e10e2a82287fdeddd0 Mon Sep 17 00:00:00 2001 From: Mark McDowall Date: Sun, 25 Feb 2024 15:16:06 -0800 Subject: [PATCH] New: Search for recently aired anime episodes with added absolute episode number Closes #2044 --- .../TvTests/RefreshEpisodeServiceFixture.cs | 28 ++++++++ src/NzbDrone.Core/Datastore/TableMapping.cs | 1 + src/NzbDrone.Core/Tv/Episode.cs | 1 + ...dService.cs => EpisodeRefreshedService.cs} | 64 +++++++++++-------- src/NzbDrone.Core/Tv/RefreshEpisodeService.cs | 8 +++ src/NzbDrone.Core/Tv/SeriesScannedHandler.cs | 8 +-- 6 files changed, 79 insertions(+), 31 deletions(-) rename src/NzbDrone.Core/Tv/{EpisodeAddedService.cs => EpisodeRefreshedService.cs} (50%) diff --git a/src/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs b/src/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs index 101305f52..99189c053 100644 --- a/src/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs +++ b/src/NzbDrone.Core.Test/TvTests/RefreshEpisodeServiceFixture.cs @@ -453,5 +453,33 @@ namespace NzbDrone.Core.Test.TvTests _updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber); _updatedEpisodes.First().AbsoluteEpisodeNumber.Should().Be(episodes[1].AbsoluteEpisodeNumber); } + + [Test] + public void should_mark_updated_episodes_that_have_newly_added_absolute_episode_number() + { + var episodes = Builder.CreateListOfSize(3) + .Build() + .ToList(); + + var existingEpisodes = new List + { + episodes[0], + episodes[1] + }; + + existingEpisodes[0].AbsoluteEpisodeNumber = null; + + Mocker.GetMock().Setup(c => c.GetEpisodeBySeries(It.IsAny())) + .Returns(existingEpisodes); + + Subject.RefreshEpisodeInfo(GetAnimeSeries(), episodes); + + _updatedEpisodes.First().SeasonNumber.Should().Be(episodes[1].SeasonNumber); + _updatedEpisodes.First().EpisodeNumber.Should().Be(episodes[1].EpisodeNumber); + _updatedEpisodes.First().AbsoluteEpisodeNumber.Should().NotBeNull(); + _updatedEpisodes.First().AbsoluteEpisodeNumberAdded.Should().BeTrue(); + + _insertedEpisodes.Any(e => e.AbsoluteEpisodeNumberAdded).Should().BeFalse(); + } } } diff --git a/src/NzbDrone.Core/Datastore/TableMapping.cs b/src/NzbDrone.Core/Datastore/TableMapping.cs index 6496c5466..c4ad03da5 100644 --- a/src/NzbDrone.Core/Datastore/TableMapping.cs +++ b/src/NzbDrone.Core/Datastore/TableMapping.cs @@ -126,6 +126,7 @@ namespace NzbDrone.Core.Datastore .Ignore(e => e.SeriesTitle) .Ignore(e => e.Series) .Ignore(e => e.HasFile) + .Ignore(e => e.AbsoluteEpisodeNumberAdded) .HasOne(s => s.EpisodeFile, s => s.EpisodeFileId); Mapper.Entity("QualityDefinitions").RegisterModel() diff --git a/src/NzbDrone.Core/Tv/Episode.cs b/src/NzbDrone.Core/Tv/Episode.cs index 39a95db26..05384cf26 100644 --- a/src/NzbDrone.Core/Tv/Episode.cs +++ b/src/NzbDrone.Core/Tv/Episode.cs @@ -46,6 +46,7 @@ namespace NzbDrone.Core.Tv public Series Series { get; set; } public bool HasFile => EpisodeFileId > 0; + public bool AbsoluteEpisodeNumberAdded { get; set; } public override string ToString() { diff --git a/src/NzbDrone.Core/Tv/EpisodeAddedService.cs b/src/NzbDrone.Core/Tv/EpisodeRefreshedService.cs similarity index 50% rename from src/NzbDrone.Core/Tv/EpisodeAddedService.cs rename to src/NzbDrone.Core/Tv/EpisodeRefreshedService.cs index f49746e65..5d2e5c1e9 100644 --- a/src/NzbDrone.Core/Tv/EpisodeAddedService.cs +++ b/src/NzbDrone.Core/Tv/EpisodeRefreshedService.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using NLog; @@ -11,19 +11,19 @@ using NzbDrone.Core.Tv.Events; namespace NzbDrone.Core.Tv { - public interface IEpisodeAddedService + public interface IEpisodeRefreshedService { - void SearchForRecentlyAdded(int seriesId); + void Search(int seriesId); } - public class EpisodeAddedService : IHandle, IEpisodeAddedService + public class EpisodeRefreshedService : IEpisodeRefreshedService, IHandle { private readonly IManageCommandQueue _commandQueueManager; private readonly IEpisodeService _episodeService; private readonly Logger _logger; - private readonly ICached> _addedEpisodesCache; + private readonly ICached> _searchCache; - public EpisodeAddedService(ICacheManager cacheManager, + public EpisodeRefreshedService(ICacheManager cacheManager, IManageCommandQueue commandQueueManager, IEpisodeService episodeService, Logger logger) @@ -31,12 +31,12 @@ namespace NzbDrone.Core.Tv _commandQueueManager = commandQueueManager; _episodeService = episodeService; _logger = logger; - _addedEpisodesCache = cacheManager.GetCache>(GetType()); + _searchCache = cacheManager.GetCache>(GetType()); } - public void SearchForRecentlyAdded(int seriesId) + public void Search(int seriesId) { - var previouslyAired = _addedEpisodesCache.Find(seriesId.ToString()); + var previouslyAired = _searchCache.Find(seriesId.ToString()); if (previouslyAired != null && previouslyAired.Any()) { @@ -48,7 +48,7 @@ namespace NzbDrone.Core.Tv } } - _addedEpisodesCache.Remove(seriesId.ToString()); + _searchCache.Remove(seriesId.ToString()); } public void Handle(EpisodeInfoRefreshedEvent message) @@ -61,29 +61,39 @@ namespace NzbDrone.Core.Tv 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.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1)) - && a.Monitored).ToList(); + var previouslyAired = message.Added.Where(a => + a.AirDateUtc.HasValue && + a.AirDateUtc.Value.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1)) && + a.Monitored) + .ToList(); if (previouslyAired.Empty()) { _logger.Debug("Newly added episodes all air in the future"); - return; + _searchCache.Set(message.Series.Id.ToString(), previouslyAired.Select(e => e.Id).ToList()); } - _addedEpisodesCache.Set(message.Series.Id.ToString(), previouslyAired.Select(e => e.Id).ToList()); + var absoluteEpisodeNumberAdded = message.Updated.Where(a => + a.AbsoluteEpisodeNumberAdded && + a.AirDateUtc.HasValue && + a.AirDateUtc.Value.Between(DateTime.UtcNow.AddDays(-14), DateTime.UtcNow.AddDays(1)) && + a.Monitored) + .ToList(); + + if (absoluteEpisodeNumberAdded.Empty()) + { + _logger.Debug("No updated episodes recently aired and had absolute episode number added"); + } + + var toSearch = new List(); + + toSearch.AddRange(previouslyAired.Select(e => e.Id)); + toSearch.AddRange(absoluteEpisodeNumberAdded.Select(e => e.Id)); + + if (toSearch.Any()) + { + _searchCache.Set(message.Series.Id.ToString(), toSearch.Distinct().ToList()); + } } } } diff --git a/src/NzbDrone.Core/Tv/RefreshEpisodeService.cs b/src/NzbDrone.Core/Tv/RefreshEpisodeService.cs index 893e326e1..6b2b7e63f 100644 --- a/src/NzbDrone.Core/Tv/RefreshEpisodeService.cs +++ b/src/NzbDrone.Core/Tv/RefreshEpisodeService.cs @@ -59,6 +59,14 @@ namespace NzbDrone.Core.Tv { existingEpisodes.Remove(episodeToUpdate); updateList.Add(episodeToUpdate); + + // Anime series with newly added absolute episode number + if (series.SeriesType == SeriesTypes.Anime && + !episodeToUpdate.AbsoluteEpisodeNumber.HasValue && + episode.AbsoluteEpisodeNumber.HasValue) + { + episodeToUpdate.AbsoluteEpisodeNumberAdded = true; + } } else { diff --git a/src/NzbDrone.Core/Tv/SeriesScannedHandler.cs b/src/NzbDrone.Core/Tv/SeriesScannedHandler.cs index f472099a9..60efcc6e5 100644 --- a/src/NzbDrone.Core/Tv/SeriesScannedHandler.cs +++ b/src/NzbDrone.Core/Tv/SeriesScannedHandler.cs @@ -13,7 +13,7 @@ namespace NzbDrone.Core.Tv private readonly IEpisodeMonitoredService _episodeMonitoredService; private readonly ISeriesService _seriesService; private readonly IManageCommandQueue _commandQueueManager; - private readonly IEpisodeAddedService _episodeAddedService; + private readonly IEpisodeRefreshedService _episodeRefreshedService; private readonly IEventAggregator _eventAggregator; private readonly Logger _logger; @@ -21,14 +21,14 @@ namespace NzbDrone.Core.Tv public SeriesScannedHandler(IEpisodeMonitoredService episodeMonitoredService, ISeriesService seriesService, IManageCommandQueue commandQueueManager, - IEpisodeAddedService episodeAddedService, + IEpisodeRefreshedService episodeRefreshedService, IEventAggregator eventAggregator, Logger logger) { _episodeMonitoredService = episodeMonitoredService; _seriesService = seriesService; _commandQueueManager = commandQueueManager; - _episodeAddedService = episodeAddedService; + _episodeRefreshedService = episodeRefreshedService; _eventAggregator = eventAggregator; _logger = logger; } @@ -39,7 +39,7 @@ namespace NzbDrone.Core.Tv if (addOptions == null) { - _episodeAddedService.SearchForRecentlyAdded(series.Id); + _episodeRefreshedService.Search(series.Id); return; }