You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Sonarr/src/NzbDrone.Core/Tv/EpisodeMonitoredService.cs

239 lines
9.6 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using NLog;
using NzbDrone.Common.Extensions;
namespace NzbDrone.Core.Tv
{
public interface IEpisodeMonitoredService
{
void SetEpisodeMonitoredStatus(Series series, MonitoringOptions monitoringOptions);
}
public class EpisodeMonitoredService : IEpisodeMonitoredService
{
private readonly ISeriesService _seriesService;
private readonly IEpisodeService _episodeService;
private readonly Logger _logger;
public EpisodeMonitoredService(ISeriesService seriesService, IEpisodeService episodeService, Logger logger)
{
_seriesService = seriesService;
_episodeService = episodeService;
_logger = logger;
}
public void SetEpisodeMonitoredStatus(Series series, MonitoringOptions monitoringOptions)
{
// Update the series without changing the episodes
if (monitoringOptions == null)
{
_seriesService.UpdateSeries(series, false);
return;
}
// Fallback for v2 endpoints
if (monitoringOptions.Monitor == MonitorTypes.Unknown)
{
LegacySetEpisodeMonitoredStatus(series, monitoringOptions);
return;
}
var firstSeason = series.Seasons.Select(s => s.SeasonNumber).Where(s => s > 0).MinOrDefault();
var lastSeason = series.Seasons.Select(s => s.SeasonNumber).MaxOrDefault();
var episodes = _episodeService.GetEpisodeBySeries(series.Id);
switch (monitoringOptions.Monitor)
{
case MonitorTypes.All:
_logger.Debug("[{0}] Monitoring all episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0);
break;
case MonitorTypes.Future:
_logger.Debug("[{0}] Monitoring future episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && (!e.AirDateUtc.HasValue || e.AirDateUtc >= DateTime.UtcNow));
break;
case MonitorTypes.Missing:
_logger.Debug("[{0}] Monitoring missing episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && !e.HasFile);
break;
case MonitorTypes.Existing:
_logger.Debug("[{0}] Monitoring existing episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && e.HasFile);
break;
case MonitorTypes.Pilot:
_logger.Debug("[{0}] Monitoring first episode episodes", series.Title);
ToggleEpisodesMonitoredState(episodes,
e => e.SeasonNumber > 0 && e.SeasonNumber == firstSeason && e.EpisodeNumber == 1);
break;
case MonitorTypes.FirstSeason:
_logger.Debug("[{0}] Monitoring first season episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && e.SeasonNumber == firstSeason);
break;
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;
}
_logger.Debug("[{0}] Monitoring latest season episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => e.SeasonNumber > 0 && e.SeasonNumber == lastSeason);
break;
case MonitorTypes.MonitorSpecials:
_logger.Debug("[{0}] Monitoring special episodes", series.Title);
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == 0), true);
break;
case MonitorTypes.UnmonitorSpecials:
_logger.Debug("[{0}] Unmonitoring special episodes", series.Title);
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == 0), false);
break;
case MonitorTypes.None:
_logger.Debug("[{0}] Unmonitoring all episodes", series.Title);
ToggleEpisodesMonitoredState(episodes, e => false);
break;
}
var monitoredSeasons = episodes.Where(e => e.Monitored)
.Select(e => e.SeasonNumber)
.Distinct()
.ToList();
foreach (var season in series.Seasons)
{
var seasonNumber = season.SeasonNumber;
// Monitor the season when:
// - Not specials
// - The latest season
// - Not only supposed to monitor the first season
if (seasonNumber > 0 &&
seasonNumber == lastSeason &&
monitoringOptions.Monitor != MonitorTypes.FirstSeason &&
monitoringOptions.Monitor != MonitorTypes.Pilot &&
monitoringOptions.Monitor != MonitorTypes.None)
{
season.Monitored = true;
}
else if (seasonNumber == firstSeason && monitoringOptions.Monitor == MonitorTypes.Pilot)
{
// Don't monitor season 1 if only the pilot episode is monitored
season.Monitored = false;
}
else if (monitoredSeasons.Contains(seasonNumber))
{
// Monitor the season if it has any monitor episodes
season.Monitored = true;
}
// Don't monitor the season
else
{
season.Monitored = false;
}
}
_episodeService.UpdateEpisodes(episodes);
_seriesService.UpdateSeries(series, false);
}
private void LegacySetEpisodeMonitoredStatus(Series series, MonitoringOptions monitoringOptions)
{
_logger.Debug("[{0}] Setting episode monitored status.", series.Title);
var episodes = _episodeService.GetEpisodeBySeries(series.Id);
if (monitoringOptions.IgnoreEpisodesWithFiles)
{
_logger.Debug("Unmonitoring Episodes with Files");
ToggleEpisodesMonitoredState(episodes.Where(e => e.HasFile), false);
}
else
{
_logger.Debug("Monitoring Episodes with Files");
ToggleEpisodesMonitoredState(episodes.Where(e => e.HasFile), true);
}
if (monitoringOptions.IgnoreEpisodesWithoutFiles)
{
_logger.Debug("Unmonitoring Episodes without Files");
ToggleEpisodesMonitoredState(episodes.Where(e => !e.HasFile && e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow)), false);
}
else
{
_logger.Debug("Monitoring Episodes without Files");
ToggleEpisodesMonitoredState(episodes.Where(e => !e.HasFile && e.AirDateUtc.HasValue && e.AirDateUtc.Value.Before(DateTime.UtcNow)), true);
}
var lastSeason = series.Seasons.Select(s => s.SeasonNumber).MaxOrDefault();
foreach (var s in series.Seasons)
{
var season = s;
// If the season is unmonitored we should unmonitor all episodes in that season
if (!season.Monitored)
{
_logger.Debug("Unmonitoring all episodes in season {0}", season.SeasonNumber);
ToggleEpisodesMonitoredState(episodes.Where(e => e.SeasonNumber == season.SeasonNumber), false);
}
// If the season is not the latest season and all it's episodes are unmonitored the season will be unmonitored
if (season.SeasonNumber < lastSeason)
{
if (episodes.Where(e => e.SeasonNumber == season.SeasonNumber).All(e => !e.Monitored))
{
_logger.Debug("Unmonitoring season {0} because all episodes are not monitored", season.SeasonNumber);
season.Monitored = false;
}
}
}
_episodeService.UpdateEpisodes(episodes);
_seriesService.UpdateSeries(series, false);
}
private void ToggleEpisodesMonitoredState(IEnumerable<Episode> episodes, bool monitored)
{
foreach (var episode in episodes)
{
episode.Monitored = monitored;
}
}
private void ToggleEpisodesMonitoredState(List<Episode> episodes, Func<Episode, bool> predicate)
{
ToggleEpisodesMonitoredState(episodes.Where(predicate), true);
ToggleEpisodesMonitoredState(episodes.Where(e => !predicate(e)), false);
}
}
}