Last Season and Recent Episodes

New: Added option to only monitor recent episodes
Fixed: Last Season always monitors the whole season
Closes #6175
This commit is contained in:
Mark McDowall 2023-11-18 14:35:03 -08:00 committed by Mark McDowall
parent ade40b72bc
commit 5d86329c18
7 changed files with 129 additions and 29 deletions

View File

@ -26,14 +26,24 @@ function SeriesMonitoringOptionsPopoverContent() {
data={translate('MonitorExistingEpisodesDescription')}
/>
<DescriptionListItem
title={translate('MonitorRecentEpisodes')}
data={translate('MonitorRecentEpisodesDescription')}
/>
<DescriptionListItem
title={translate('MonitorPilotEpisode')}
data={translate('MonitorPilotEpisodeDescription')}
/>
<DescriptionListItem
title={translate('MonitorFirstSeason')}
data={translate('MonitorFirstSeasonDescription')}
/>
<DescriptionListItem
title={translate('MonitorLatestSeason')}
data={translate('MonitorLatestSeasonDescription')}
title={translate('MonitorLastSeason')}
data={translate('MonitorLastSeasonDescription')}
/>
<DescriptionListItem

View File

@ -25,6 +25,12 @@ const monitorOptions = [
return translate('MonitorExistingEpisodes');
}
},
{
key: 'recent',
get value() {
return translate('MonitorRecentEpisodes');
}
},
{
key: 'pilot',
get value() {
@ -38,9 +44,9 @@ const monitorOptions = [
}
},
{
key: 'latestSeason',
key: 'lastSeason',
get value() {
return translate('MonitorLatestSeason');
return translate('MonitorLastSeason');
}
},
{

View File

@ -202,7 +202,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
}
[Test]
public void should_not_monitor_season_when_all_episodes_are_monitored_except_latest_season()
public void should_not_monitor_season_when_all_episodes_are_monitored_except_last_season()
{
_series.Seasons = Builder<Season>.CreateListOfSize(2)
.All()
@ -226,7 +226,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
var monitoringOptions = new MonitoringOptions
{
Monitor = MonitorTypes.LatestSeason
Monitor = MonitorTypes.LastSeason
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
@ -264,13 +264,47 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
}
[Test]
public void should_not_monitor_latest_season_if_all_episodes_aired_more_than_90_days_ago()
public void should_monitor_last_season_if_all_episodes_aired_more_than_90_days_ago()
{
_series.Seasons = Builder<Season>.CreateListOfSize(2)
.All()
.With(n => n.Monitored = true)
.Build()
.ToList();
_episodes = Builder<Episode>.CreateListOfSize(5)
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-200))
.TheLast(2)
.With(e => e.SeasonNumber = 2)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-100))
.Build()
.ToList();
var monitoringOptions = new MonitoringOptions
{
Monitor = MonitorTypes.LastSeason
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifySeasonMonitored(n => n.SeasonNumber == 2);
VerifyMonitored(n => n.SeasonNumber == 2);
VerifySeasonNotMonitored(n => n.SeasonNumber == 1);
VerifyNotMonitored(n => n.SeasonNumber == 1);
}
[Test]
public void should_not_monitor_any_recent_episodes_if_all_episodes_aired_more_than_90_days_ago()
{
_episodes.ForEach(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-100));
var monitoringOptions = new MonitoringOptions
{
Monitor = MonitorTypes.LatestSeason
Monitor = MonitorTypes.Recent
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
@ -279,6 +313,43 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
.Verify(v => v.UpdateEpisodes(It.Is<List<Episode>>(l => l.All(e => !e.Monitored))));
}
[Test]
public void should_monitor_any_recent_and_future_episodes_if_all_episodes_aired_within_90_days()
{
_series.Seasons = Builder<Season>.CreateListOfSize(1)
.All()
.With(n => n.Monitored = true)
.Build()
.ToList();
_episodes = Builder<Episode>.CreateListOfSize(5)
.All()
.With(e => e.SeasonNumber = 1)
.With(e => e.EpisodeFileId = 0)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-200))
.TheLast(3)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(-5))
.TheLast(1)
.With(e => e.AirDateUtc = DateTime.UtcNow.AddDays(30))
.Build()
.ToList();
Mocker.GetMock<IEpisodeService>()
.Setup(s => s.GetEpisodeBySeries(It.IsAny<int>()))
.Returns(_episodes);
var monitoringOptions = new MonitoringOptions
{
Monitor = MonitorTypes.Recent
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);
VerifySeasonMonitored(n => n.SeasonNumber == 1);
VerifyNotMonitored(n => n.AirDateUtc.HasValue && n.AirDateUtc.Value.Before(DateTime.UtcNow.AddDays(-90)));
VerifyMonitored(n => n.AirDateUtc.HasValue && n.AirDateUtc.Value.After(DateTime.UtcNow.AddDays(-90)));
}
[Test]
public void should_monitor_latest_season_if_some_episodes_have_aired()
{
@ -302,7 +373,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeMonitoredServiceTests
var monitoringOptions = new MonitoringOptions
{
Monitor = MonitorTypes.LatestSeason
Monitor = MonitorTypes.LastSeason
};
Subject.SetEpisodeMonitoredStatus(_series, monitoringOptions);

View File

@ -984,8 +984,8 @@
"MonitorFirstSeasonDescription": "Monitor all episodes of the first season. All other seasons will be ignored",
"MonitorFutureEpisodes": "Future Episodes",
"MonitorFutureEpisodesDescription": "Monitor episodes that have not aired yet",
"MonitorLatestSeason": "Latest Season",
"MonitorLatestSeasonDescription": "Monitor all episodes of the latest season that aired within the last 90 days and all future seasons",
"MonitorLastSeason": "Last Season",
"MonitorLastSeasonDescription": "Monitor all episodes of the last season",
"MonitorMissingEpisodes": "Missing Episodes",
"MonitorMissingEpisodesDescription": "Monitor episodes that do not have files or have not aired yet",
"MonitorNewSeasons": "Monitor New Seasons",
@ -994,6 +994,9 @@
"MonitorNone": "None",
"MonitorNoneDescription": "No episodes will be monitored",
"MonitorPilotEpisode": "Pilot Episode",
"MonitorPilotEpisodeDescription": "Only monitor the first episode of the first season",
"MonitorRecentEpisodes": "Recent Episodes",
"MonitorRecentEpisodesDescription": "Monitor episodes aired within the last 90 days and future episodes",
"MonitorSelected": "Monitor Selected",
"MonitorSeries": "Monitor Series",
"MonitorSpecials": "Monitor Specials",

View File

@ -83,23 +83,27 @@ namespace NzbDrone.Core.Tv
break;
case MonitorTypes.LastSeason:
#pragma warning disable CS0612
case MonitorTypes.LatestSeason:
if (episodes.Where(e => e.SeasonNumber == lastSeason)
.All(e => e.AirDateUtc.HasValue &&
e.AirDateUtc.Value.Before(DateTime.UtcNow) &&
!e.AirDateUtc.Value.InLastDays(90)))
{
_logger.Debug("[{0}] Unmonitoring all episodes because latest season aired more than 90 days ago", series.Title);
ToggleEpisodesMonitoredState(episodes, e => false);
break;
}
#pragma warning restore CS0612
_logger.Debug("[{0}] Monitoring latest season episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && e.SeasonNumber == lastSeason);
break;
case MonitorTypes.Recent:
_logger.Debug("[{0}] Monitoring recent and future episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 &&
(!e.AirDateUtc.HasValue || (
e.AirDateUtc.Value.Before(DateTime.UtcNow) &&
e.AirDateUtc.Value.InLastDays(90))
|| e.AirDateUtc.Value.After(DateTime.UtcNow)));
break;
case MonitorTypes.MonitorSpecials:
_logger.Debug("[{0}] Monitoring special episodes", series.Title);
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == 0), true);
@ -128,15 +132,14 @@ namespace NzbDrone.Core.Tv
{
var seasonNumber = season.SeasonNumber;
// Monitor the season when:
// Monitor the last season when:
// - Not specials
// - The latest season
// - Not only supposed to monitor the first season
// - Set to monitor all or future episodes
if (seasonNumber > 0 &&
seasonNumber == lastSeason &&
monitoringOptions.Monitor != MonitorTypes.FirstSeason &&
monitoringOptions.Monitor != MonitorTypes.Pilot &&
monitoringOptions.Monitor != MonitorTypes.None)
(monitoringOptions.Monitor == MonitorTypes.All ||
monitoringOptions.Monitor == MonitorTypes.Future))
{
season.Monitored = true;
}

View File

@ -1,3 +1,4 @@
using System;
using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Tv
@ -17,8 +18,13 @@ namespace NzbDrone.Core.Tv
Missing,
Existing,
FirstSeason,
LastSeason,
[Obsolete]
LatestSeason,
Pilot,
Recent,
MonitorSpecials,
UnmonitorSpecials,
None

View File

@ -139,7 +139,6 @@ namespace NzbDrone.Core.Tv
{
var existingSeason = series.Seasons.FirstOrDefault(s => s.SeasonNumber == season.SeasonNumber);
// Todo: Should this should use the previous season's monitored state?
if (existingSeason == null)
{
if (season.SeasonNumber == 0)
@ -149,8 +148,10 @@ namespace NzbDrone.Core.Tv
continue;
}
_logger.Debug("New season ({0}) for series: [{1}] {2}, setting monitored to {3}", season.SeasonNumber, series.TvdbId, series.Title, series.Monitored.ToString().ToLowerInvariant());
season.Monitored = series.Monitored;
var monitorNewSeasons = series.MonitorNewItems == NewItemMonitorTypes.All;
_logger.Debug("New season ({0}) for series: [{1}] {2}, setting monitored to {3}", season.SeasonNumber, series.TvdbId, series.Title, monitorNewSeasons.ToString().ToLowerInvariant());
season.Monitored = monitorNewSeasons;
}
else
{