New: Do not automatically unmonitor episodes renamed outside of Sonarr
Closes #6584
This commit is contained in:
parent
653963a247
commit
fa4c11a943
|
@ -1,9 +1,11 @@
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using FizzWare.NBuilder;
|
using FizzWare.NBuilder;
|
||||||
using Moq;
|
using Moq;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
using NzbDrone.Core.MediaFiles.Events;
|
using NzbDrone.Core.MediaFiles.Events;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
|
@ -14,14 +16,20 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||||
[TestFixture]
|
[TestFixture]
|
||||||
public class HandleEpisodeFileDeletedFixture : CoreTest<EpisodeService>
|
public class HandleEpisodeFileDeletedFixture : CoreTest<EpisodeService>
|
||||||
{
|
{
|
||||||
|
private Series _series;
|
||||||
private EpisodeFile _episodeFile;
|
private EpisodeFile _episodeFile;
|
||||||
private List<Episode> _episodes;
|
private List<Episode> _episodes;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void Setup()
|
public void Setup()
|
||||||
{
|
{
|
||||||
|
_series = Builder<Series>
|
||||||
|
.CreateNew()
|
||||||
|
.Build();
|
||||||
|
|
||||||
_episodeFile = Builder<EpisodeFile>
|
_episodeFile = Builder<EpisodeFile>
|
||||||
.CreateNew()
|
.CreateNew()
|
||||||
|
.With(e => e.SeriesId = _series.Id)
|
||||||
.Build();
|
.Build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,6 +38,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||||
_episodes = Builder<Episode>
|
_episodes = Builder<Episode>
|
||||||
.CreateListOfSize(1)
|
.CreateListOfSize(1)
|
||||||
.All()
|
.All()
|
||||||
|
.With(e => e.SeriesId = _series.Id)
|
||||||
.With(e => e.Monitored = true)
|
.With(e => e.Monitored = true)
|
||||||
.Build()
|
.Build()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -44,6 +53,7 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||||
_episodes = Builder<Episode>
|
_episodes = Builder<Episode>
|
||||||
.CreateListOfSize(2)
|
.CreateListOfSize(2)
|
||||||
.All()
|
.All()
|
||||||
|
.With(e => e.SeriesId = _series.Id)
|
||||||
.With(e => e.Monitored = true)
|
.With(e => e.Monitored = true)
|
||||||
.Build()
|
.Build()
|
||||||
.ToList();
|
.ToList();
|
||||||
|
@ -85,9 +95,31 @@ namespace NzbDrone.Core.Test.TvTests.EpisodeServiceTests
|
||||||
.Returns(true);
|
.Returns(true);
|
||||||
|
|
||||||
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
||||||
|
Subject.HandleAsync(new SeriesScannedEvent(_series, new List<string>()));
|
||||||
|
|
||||||
Mocker.GetMock<IEpisodeRepository>()
|
Mocker.GetMock<IEpisodeRepository>()
|
||||||
.Verify(v => v.ClearFileId(It.IsAny<Episode>(), true), Times.Once());
|
.Verify(v => v.SetMonitored(It.IsAny<IEnumerable<int>>(), false), Times.Once());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_leave_monitored_if_autoUnmonitor_is_true_and_missing_episode_is_replaced()
|
||||||
|
{
|
||||||
|
GivenSingleEpisodeFile();
|
||||||
|
|
||||||
|
var newEpisodeFile = _episodeFile.JsonClone();
|
||||||
|
newEpisodeFile.Id = 123;
|
||||||
|
newEpisodeFile.Episodes = new LazyLoaded<List<Episode>>(_episodes);
|
||||||
|
|
||||||
|
Mocker.GetMock<IConfigService>()
|
||||||
|
.SetupGet(s => s.AutoUnmonitorPreviouslyDownloadedEpisodes)
|
||||||
|
.Returns(true);
|
||||||
|
|
||||||
|
Subject.Handle(new EpisodeFileDeletedEvent(_episodeFile, DeleteMediaFileReason.MissingFromDisk));
|
||||||
|
Subject.Handle(new EpisodeFileAddedEvent(newEpisodeFile));
|
||||||
|
Subject.HandleAsync(new SeriesScannedEvent(_series, new List<string>()));
|
||||||
|
|
||||||
|
Mocker.GetMock<IEpisodeRepository>()
|
||||||
|
.Verify(v => v.SetMonitored(It.IsAny<IEnumerable<int>>(), false), Times.Never());
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
|
|
|
@ -2,6 +2,7 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using NLog;
|
using NLog;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
using NzbDrone.Core.Datastore;
|
using NzbDrone.Core.Datastore;
|
||||||
using NzbDrone.Core.MediaFiles;
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
@ -42,16 +43,19 @@ namespace NzbDrone.Core.Tv
|
||||||
public class EpisodeService : IEpisodeService,
|
public class EpisodeService : IEpisodeService,
|
||||||
IHandle<EpisodeFileDeletedEvent>,
|
IHandle<EpisodeFileDeletedEvent>,
|
||||||
IHandle<EpisodeFileAddedEvent>,
|
IHandle<EpisodeFileAddedEvent>,
|
||||||
IHandleAsync<SeriesDeletedEvent>
|
IHandleAsync<SeriesDeletedEvent>,
|
||||||
|
IHandleAsync<SeriesScannedEvent>
|
||||||
{
|
{
|
||||||
private readonly IEpisodeRepository _episodeRepository;
|
private readonly IEpisodeRepository _episodeRepository;
|
||||||
private readonly IConfigService _configService;
|
private readonly IConfigService _configService;
|
||||||
|
private readonly ICached<HashSet<int>> _cache;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
public EpisodeService(IEpisodeRepository episodeRepository, IConfigService configService, Logger logger)
|
public EpisodeService(IEpisodeRepository episodeRepository, IConfigService configService, ICacheManager cacheManager, Logger logger)
|
||||||
{
|
{
|
||||||
_episodeRepository = episodeRepository;
|
_episodeRepository = episodeRepository;
|
||||||
_configService = configService;
|
_configService = configService;
|
||||||
|
_cache = cacheManager.GetCache<HashSet<int>>(GetType());
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,34 +219,6 @@ namespace NzbDrone.Core.Tv
|
||||||
_episodeRepository.DeleteMany(episodes);
|
_episodeRepository.DeleteMany(episodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void HandleAsync(SeriesDeletedEvent message)
|
|
||||||
{
|
|
||||||
var episodes = _episodeRepository.GetEpisodesBySeriesIds(message.Series.Select(s => s.Id).ToList());
|
|
||||||
_episodeRepository.DeleteMany(episodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeFileDeletedEvent message)
|
|
||||||
{
|
|
||||||
foreach (var episode in GetEpisodesByFileId(message.EpisodeFile.Id))
|
|
||||||
{
|
|
||||||
_logger.Debug("Detaching episode {0} from file.", episode.Id);
|
|
||||||
|
|
||||||
var unmonitorForReason = message.Reason != DeleteMediaFileReason.Upgrade &&
|
|
||||||
message.Reason != DeleteMediaFileReason.ManualOverride;
|
|
||||||
|
|
||||||
_episodeRepository.ClearFileId(episode, unmonitorForReason && _configService.AutoUnmonitorPreviouslyDownloadedEpisodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Handle(EpisodeFileAddedEvent message)
|
|
||||||
{
|
|
||||||
foreach (var episode in message.EpisodeFile.Episodes.Value)
|
|
||||||
{
|
|
||||||
_episodeRepository.SetFileId(episode, message.EpisodeFile.Id);
|
|
||||||
_logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.RelativePath, episode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Episode FindOneByAirDate(int seriesId, string date, int? part)
|
private Episode FindOneByAirDate(int seriesId, string date, int? part)
|
||||||
{
|
{
|
||||||
var episodes = _episodeRepository.Find(seriesId, date);
|
var episodes = _episodeRepository.Find(seriesId, date);
|
||||||
|
@ -277,5 +253,73 @@ namespace NzbDrone.Core.Tv
|
||||||
|
|
||||||
throw new InvalidOperationException($"Multiple episodes with the same air date found. Date: {date}");
|
throw new InvalidOperationException($"Multiple episodes with the same air date found. Date: {date}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Handle(EpisodeFileDeletedEvent message)
|
||||||
|
{
|
||||||
|
foreach (var episode in GetEpisodesByFileId(message.EpisodeFile.Id))
|
||||||
|
{
|
||||||
|
_logger.Debug("Detaching episode {0} from file.", episode.Id);
|
||||||
|
|
||||||
|
var unmonitorEpisodes = _configService.AutoUnmonitorPreviouslyDownloadedEpisodes;
|
||||||
|
|
||||||
|
var unmonitorForReason = message.Reason != DeleteMediaFileReason.Upgrade &&
|
||||||
|
message.Reason != DeleteMediaFileReason.ManualOverride &&
|
||||||
|
message.Reason != DeleteMediaFileReason.MissingFromDisk;
|
||||||
|
|
||||||
|
// If episode is being unlinked because it's missing from disk store it for
|
||||||
|
if (message.Reason == DeleteMediaFileReason.MissingFromDisk && unmonitorEpisodes)
|
||||||
|
{
|
||||||
|
lock (_cache)
|
||||||
|
{
|
||||||
|
var ids = _cache.Get(episode.SeriesId.ToString(), () => new HashSet<int>());
|
||||||
|
|
||||||
|
ids.Add(episode.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_episodeRepository.ClearFileId(episode, unmonitorForReason && unmonitorEpisodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(EpisodeFileAddedEvent message)
|
||||||
|
{
|
||||||
|
foreach (var episode in message.EpisodeFile.Episodes.Value)
|
||||||
|
{
|
||||||
|
_episodeRepository.SetFileId(episode, message.EpisodeFile.Id);
|
||||||
|
|
||||||
|
lock (_cache)
|
||||||
|
{
|
||||||
|
var ids = _cache.Find(episode.SeriesId.ToString());
|
||||||
|
|
||||||
|
if (ids?.Contains(episode.Id) == true)
|
||||||
|
{
|
||||||
|
ids.Remove(episode.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.Debug("Linking [{0}] > [{1}]", message.EpisodeFile.RelativePath, episode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleAsync(SeriesDeletedEvent message)
|
||||||
|
{
|
||||||
|
var episodes = _episodeRepository.GetEpisodesBySeriesIds(message.Series.Select(s => s.Id).ToList());
|
||||||
|
_episodeRepository.DeleteMany(episodes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleAsync(SeriesScannedEvent message)
|
||||||
|
{
|
||||||
|
lock (_cache)
|
||||||
|
{
|
||||||
|
var ids = _cache.Find(message.Series.Id.ToString());
|
||||||
|
|
||||||
|
if (ids?.Any() == true)
|
||||||
|
{
|
||||||
|
_episodeRepository.SetMonitored(ids, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_cache.Remove(message.Series.Id.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue