Fixed: Errors loading queue after episodes in series are removed
Closes #3565
This commit is contained in:
parent
54a267d860
commit
6c324c8a1c
|
@ -11,6 +11,7 @@ using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Core.Tv;
|
using NzbDrone.Core.Tv;
|
||||||
using NzbDrone.Core.Indexers;
|
using NzbDrone.Core.Indexers;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using NzbDrone.Core.Tv.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Test.Download.TrackedDownloads
|
namespace NzbDrone.Core.Test.Download.TrackedDownloads
|
||||||
{
|
{
|
||||||
|
@ -144,5 +145,185 @@ namespace NzbDrone.Core.Test.Download.TrackedDownloads
|
||||||
trackedDownload.RemoteEpisode.ParsedEpisodeInfo.SeasonNumber.Should().Be(0);
|
trackedDownload.RemoteEpisode.ParsedEpisodeInfo.SeasonNumber.Should().Be(0);
|
||||||
trackedDownload.RemoteEpisode.MappedSeasonNumber.Should().Be(0);
|
trackedDownload.RemoteEpisode.MappedSeasonNumber.Should().Be(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_unmap_tracked_download_if_episode_deleted()
|
||||||
|
{
|
||||||
|
GivenDownloadHistory();
|
||||||
|
|
||||||
|
var remoteEpisode = new RemoteEpisode
|
||||||
|
{
|
||||||
|
Series = new Series() { Id = 5 },
|
||||||
|
Episodes = new List<Episode> { new Episode { Id = 4 } },
|
||||||
|
ParsedEpisodeInfo = new ParsedEpisodeInfo()
|
||||||
|
{
|
||||||
|
SeriesTitle = "TV Series",
|
||||||
|
SeasonNumber = 1,
|
||||||
|
EpisodeNumbers = new[] { 1 }
|
||||||
|
},
|
||||||
|
MappedSeasonNumber = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(remoteEpisode);
|
||||||
|
|
||||||
|
Mocker.GetMock<IHistoryService>()
|
||||||
|
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
|
||||||
|
.Returns(new List<EpisodeHistory>());
|
||||||
|
|
||||||
|
|
||||||
|
var client = new DownloadClientDefinition()
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
};
|
||||||
|
|
||||||
|
var item = new DownloadClientItem()
|
||||||
|
{
|
||||||
|
Title = "TV Series - S01E01",
|
||||||
|
DownloadId = "12345",
|
||||||
|
DownloadClientInfo = new DownloadClientItemClientInfo
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Type = "Blackhole",
|
||||||
|
Name = "Blackhole Client",
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.TrackDownload(client, item);
|
||||||
|
Subject.GetTrackedDownloads().Should().HaveCount(1);
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(default(RemoteEpisode));
|
||||||
|
|
||||||
|
Subject.Handle(new EpisodeInfoRefreshedEvent(remoteEpisode.Series, new List<Episode>(), new List<Episode>(), remoteEpisode.Episodes));
|
||||||
|
|
||||||
|
var trackedDownloads = Subject.GetTrackedDownloads();
|
||||||
|
trackedDownloads.Should().HaveCount(1);
|
||||||
|
trackedDownloads.First().RemoteEpisode.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_throw_when_processing_deleted_episodes()
|
||||||
|
{
|
||||||
|
GivenDownloadHistory();
|
||||||
|
|
||||||
|
var remoteEpisode = new RemoteEpisode
|
||||||
|
{
|
||||||
|
Series = new Series() { Id = 5 },
|
||||||
|
Episodes = new List<Episode> { new Episode { Id = 4 } },
|
||||||
|
ParsedEpisodeInfo = new ParsedEpisodeInfo()
|
||||||
|
{
|
||||||
|
SeriesTitle = "TV Series",
|
||||||
|
SeasonNumber = 1,
|
||||||
|
EpisodeNumbers = new[] { 1 }
|
||||||
|
},
|
||||||
|
MappedSeasonNumber = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(default(RemoteEpisode));
|
||||||
|
|
||||||
|
Mocker.GetMock<IHistoryService>()
|
||||||
|
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
|
||||||
|
.Returns(new List<EpisodeHistory>());
|
||||||
|
|
||||||
|
|
||||||
|
var client = new DownloadClientDefinition()
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
};
|
||||||
|
|
||||||
|
var item = new DownloadClientItem()
|
||||||
|
{
|
||||||
|
Title = "TV Series - S01E01",
|
||||||
|
DownloadId = "12345",
|
||||||
|
DownloadClientInfo = new DownloadClientItemClientInfo
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Type = "Blackhole",
|
||||||
|
Name = "Blackhole Client",
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.TrackDownload(client, item);
|
||||||
|
Subject.GetTrackedDownloads().Should().HaveCount(1);
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(default(RemoteEpisode));
|
||||||
|
|
||||||
|
Subject.Handle(new EpisodeInfoRefreshedEvent(remoteEpisode.Series, new List<Episode>(), new List<Episode>(), remoteEpisode.Episodes));
|
||||||
|
|
||||||
|
var trackedDownloads = Subject.GetTrackedDownloads();
|
||||||
|
trackedDownloads.Should().HaveCount(1);
|
||||||
|
trackedDownloads.First().RemoteEpisode.Should().BeNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void should_not_throw_when_processing_deleted_series()
|
||||||
|
{
|
||||||
|
GivenDownloadHistory();
|
||||||
|
|
||||||
|
var remoteEpisode = new RemoteEpisode
|
||||||
|
{
|
||||||
|
Series = new Series() { Id = 5 },
|
||||||
|
Episodes = new List<Episode> { new Episode { Id = 4 } },
|
||||||
|
ParsedEpisodeInfo = new ParsedEpisodeInfo()
|
||||||
|
{
|
||||||
|
SeriesTitle = "TV Series",
|
||||||
|
SeasonNumber = 1,
|
||||||
|
EpisodeNumbers = new[] { 1 }
|
||||||
|
},
|
||||||
|
MappedSeasonNumber = 0
|
||||||
|
};
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(default(RemoteEpisode));
|
||||||
|
|
||||||
|
Mocker.GetMock<IHistoryService>()
|
||||||
|
.Setup(s => s.FindByDownloadId(It.IsAny<string>()))
|
||||||
|
.Returns(new List<EpisodeHistory>());
|
||||||
|
|
||||||
|
|
||||||
|
var client = new DownloadClientDefinition()
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
};
|
||||||
|
|
||||||
|
var item = new DownloadClientItem()
|
||||||
|
{
|
||||||
|
Title = "TV Series - S01E01",
|
||||||
|
DownloadId = "12345",
|
||||||
|
DownloadClientInfo = new DownloadClientItemClientInfo
|
||||||
|
{
|
||||||
|
Id = 1,
|
||||||
|
Type = "Blackhole",
|
||||||
|
Name = "Blackhole Client",
|
||||||
|
Protocol = DownloadProtocol.Torrent
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Subject.TrackDownload(client, item);
|
||||||
|
Subject.GetTrackedDownloads().Should().HaveCount(1);
|
||||||
|
|
||||||
|
Mocker.GetMock<IParsingService>()
|
||||||
|
.Setup(s => s.Map(It.IsAny<ParsedEpisodeInfo>(), It.IsAny<int>(), It.IsAny<int>(), null))
|
||||||
|
.Returns(default(RemoteEpisode));
|
||||||
|
|
||||||
|
Subject.Handle(new SeriesDeletedEvent(remoteEpisode.Series, true, true));
|
||||||
|
|
||||||
|
var trackedDownloads = Subject.GetTrackedDownloads();
|
||||||
|
trackedDownloads.Should().HaveCount(1);
|
||||||
|
trackedDownloads.First().RemoteEpisode.Should().BeNull();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ using NzbDrone.Core.Download.History;
|
||||||
using NzbDrone.Core.History;
|
using NzbDrone.Core.History;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Parser;
|
using NzbDrone.Core.Parser;
|
||||||
|
using NzbDrone.Core.Tv.Events;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Download.TrackedDownloads
|
namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
{
|
{
|
||||||
|
@ -21,7 +22,9 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
void UpdateTrackable(List<TrackedDownload> trackedDownloads);
|
void UpdateTrackable(List<TrackedDownload> trackedDownloads);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TrackedDownloadService : ITrackedDownloadService
|
public class TrackedDownloadService : ITrackedDownloadService,
|
||||||
|
IHandle<EpisodeInfoRefreshedEvent>,
|
||||||
|
IHandle<SeriesDeletedEvent>
|
||||||
{
|
{
|
||||||
private readonly IParsingService _parsingService;
|
private readonly IParsingService _parsingService;
|
||||||
private readonly IHistoryService _historyService;
|
private readonly IHistoryService _historyService;
|
||||||
|
@ -187,6 +190,13 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void UpdateCachedItem(TrackedDownload trackedDownload)
|
||||||
|
{
|
||||||
|
var parsedEpisodeInfo = Parser.Parser.ParseTitle(trackedDownload.DownloadItem.Title);
|
||||||
|
|
||||||
|
trackedDownload.RemoteEpisode = parsedEpisodeInfo == null ? null :_parsingService.Map(parsedEpisodeInfo, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
|
private static TrackedDownloadState GetStateFromHistory(DownloadHistoryEventType eventType)
|
||||||
{
|
{
|
||||||
switch (eventType)
|
switch (eventType)
|
||||||
|
@ -201,5 +211,45 @@ namespace NzbDrone.Core.Download.TrackedDownloads
|
||||||
return TrackedDownloadState.Downloading;
|
return TrackedDownloadState.Downloading;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Handle(EpisodeInfoRefreshedEvent message)
|
||||||
|
{
|
||||||
|
var needsToUpdate = false;
|
||||||
|
|
||||||
|
foreach (var episode in message.Removed)
|
||||||
|
{
|
||||||
|
var cachedItems = _cache.Values.Where(t =>
|
||||||
|
t.RemoteEpisode?.Episodes != null &&
|
||||||
|
t.RemoteEpisode.Episodes.Any(e => e.Id == episode.Id))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (cachedItems.Any())
|
||||||
|
{
|
||||||
|
needsToUpdate = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedItems.ForEach(UpdateCachedItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (needsToUpdate)
|
||||||
|
{
|
||||||
|
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(SeriesDeletedEvent message)
|
||||||
|
{
|
||||||
|
var cachedItems = _cache.Values.Where(t =>
|
||||||
|
t.RemoteEpisode?.Series != null &&
|
||||||
|
t.RemoteEpisode.Series.Id == message.Series.Id)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
if (cachedItems.Any())
|
||||||
|
{
|
||||||
|
cachedItems.ForEach(UpdateCachedItem);
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new TrackedDownloadRefreshedEvent(GetTrackedDownloads()));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,13 @@ namespace NzbDrone.Core.Tv.Events
|
||||||
public Series Series { get; set; }
|
public Series Series { get; set; }
|
||||||
public ReadOnlyCollection<Episode> Added { get; private set; }
|
public ReadOnlyCollection<Episode> Added { get; private set; }
|
||||||
public ReadOnlyCollection<Episode> Updated { get; private set; }
|
public ReadOnlyCollection<Episode> Updated { get; private set; }
|
||||||
|
public ReadOnlyCollection<Episode> Removed { get; private set; }
|
||||||
|
|
||||||
public EpisodeInfoRefreshedEvent(Series series, IList<Episode> added, IList<Episode> updated)
|
public EpisodeInfoRefreshedEvent(Series series, IList<Episode> added, IList<Episode> updated, IList<Episode> removed)
|
||||||
{
|
{
|
||||||
Series = series;
|
Series = series;
|
||||||
Added = new ReadOnlyCollection<Episode>(added);
|
Added = new ReadOnlyCollection<Episode>(added);
|
||||||
Updated = new ReadOnlyCollection<Episode>(updated);
|
Removed = new ReadOnlyCollection<Episode>(removed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -99,7 +99,7 @@ namespace NzbDrone.Core.Tv
|
||||||
_episodeService.UpdateMany(updateList);
|
_episodeService.UpdateMany(updateList);
|
||||||
_episodeService.InsertMany(newList);
|
_episodeService.InsertMany(newList);
|
||||||
|
|
||||||
_eventAggregator.PublishEvent(new EpisodeInfoRefreshedEvent(series, newList, updateList));
|
_eventAggregator.PublishEvent(new EpisodeInfoRefreshedEvent(series, newList, updateList, existingEpisodes));
|
||||||
|
|
||||||
if (failCount != 0)
|
if (failCount != 0)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue