1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-05 02:22:31 +01:00

Show title mismatches, but don't import them automaticallys

Fixed: Show Series title mismatches in the UI
Fixed: Force import from Queue for title mismatches
This commit is contained in:
Mark McDowall 2015-01-19 15:53:31 -08:00
parent 7f27507ef6
commit 2bbce39faa
11 changed files with 154 additions and 68 deletions

View File

@ -72,7 +72,7 @@ private JsonResponse<QueueResource> Import()
var resource = Request.Body.FromJson<QueueResource>();
var trackedDownload = GetTrackedDownload(resource.Id);
_completedDownloadService.Process(trackedDownload);
_completedDownloadService.Process(trackedDownload, true);
return resource.AsResponse();
}

View File

@ -11,6 +11,7 @@
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
@ -65,18 +66,24 @@ private void GivenNoGrabbedHistory()
.Setup(s => s.MostRecentForDownloadId(_trackedDownload.DownloadItem.DownloadId))
.Returns((History.History)null);
}
private void GivenSuccessfulImport()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()))
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }))
});
}
private void GivenSeriesMatch()
{
Mocker.GetMock<IParsingService>()
.Setup(s => s.GetSeries(It.IsAny<string>()))
.Returns(_trackedDownload.RemoteEpisode.Series);
}
[TestCase(DownloadItemStatus.Downloading)]
[TestCase(DownloadItemStatus.Failed)]
[TestCase(DownloadItemStatus.Queued)]
@ -107,8 +114,9 @@ public void should_process_if_matching_history_is_not_found_but_category_specifi
{
_trackedDownload.DownloadItem.Category = "tv";
GivenNoGrabbedHistory();
GivenSeriesMatch();
GivenSuccessfulImport();
Subject.Process(_trackedDownload);
AssertCompletedDownload();
@ -142,7 +150,7 @@ public void should_not_process_if_output_path_is_empty()
public void should_not_mark_as_imported_if_all_files_were_rejected()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()))
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}, "Rejected!"),"Test Failure"),
@ -161,7 +169,7 @@ public void should_not_mark_as_imported_if_all_files_were_rejected()
public void should_not_mark_as_imported_if_all_files_were_skipped()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()))
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}),"Test Failure"),
@ -177,6 +185,7 @@ public void should_not_mark_as_imported_if_all_files_were_skipped()
[Test]
public void should_mark_as_imported_if_all_episodes_were_imported_but_extra_files_were_not()
{
GivenSeriesMatch();
_trackedDownload.RemoteEpisode.Episodes = new List<Episode>
{
@ -184,14 +193,13 @@ public void should_mark_as_imported_if_all_episodes_were_imported_but_extra_file
};
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()))
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"})),
new ImportResult(new ImportDecision(new LocalEpisode{Path = @"C:\TestPath\Droned.S01E01.mkv"}),"Test Failure")
});
Subject.Process(_trackedDownload);
AssertCompletedDownload();
@ -208,7 +216,7 @@ public void should_mark_as_failed_if_some_of_episodes_were_not_imported()
};
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()))
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"})),
@ -222,11 +230,38 @@ public void should_mark_as_failed_if_some_of_episodes_were_not_imported()
AssertNoCompletedDownload();
}
[Test]
public void should_not_import_when_there_is_a_title_mismatch()
{
Subject.Process(_trackedDownload);
AssertNoCompletedDownload();
}
[Test]
public void should_mark_as_import_title_mismatch_if_ignore_warnings_is_true()
{
_trackedDownload.RemoteEpisode.Episodes = new List<Episode>
{
new Episode()
};
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>
{
new ImportResult(new ImportDecision(new LocalEpisode {Path = @"C:\TestPath\Droned.S01E01.mkv"}))
});
Subject.Process(_trackedDownload, true);
AssertCompletedDownload();
}
private void AssertNoAttemptedImport()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Verify(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<DownloadClientItem>()), Times.Never());
.Verify(v => v.ProcessPath(It.IsAny<string>(), It.IsAny<Series>(), It.IsAny<DownloadClientItem>()), Times.Never());
AssertNoCompletedDownload();
}
@ -242,7 +277,7 @@ private void AssertNoCompletedDownload()
private void AssertCompletedDownload()
{
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Verify(v => v.ProcessPath(_trackedDownload.DownloadItem.OutputPath.FullPath, _trackedDownload.DownloadItem), Times.Once());
.Verify(v => v.ProcessPath(_trackedDownload.DownloadItem.OutputPath.FullPath, _trackedDownload.RemoteEpisode.Series, _trackedDownload.DownloadItem), Times.Once());
_trackedDownload.State.Should().Be(TrackedDownloadStage.Imported);
}

View File

@ -19,7 +19,6 @@ public class DownloadedEpisodesCommandServiceFixture : CoreTest<DownloadedEpisod
{
private string _droneFactory = "c:\\drop\\".AsOsAgnostic();
[SetUp]
public void Setup()
{
@ -32,11 +31,6 @@ public void Setup()
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessRootFolder(It.IsAny<DirectoryInfo>()))
.Returns(new List<ImportResult>());
Mocker.GetMock<IDownloadedEpisodesImportService>()
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
.Returns(new List<ImportResult>());
}
[Test]
@ -48,7 +42,7 @@ public void should_process_dronefactory_if_path_is_not_specified()
}
[Test]
public void should_skip_import_if_dropfolder_doesnt_exist()
public void should_skip_import_if_dronefactory_doesnt_exist()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>())).Returns(false);
@ -57,7 +51,6 @@ public void should_skip_import_if_dropfolder_doesnt_exist()
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessRootFolder(It.IsAny<DirectoryInfo>()), Times.Never());
ExceptionVerification.ExpectedWarns(1);
}
}
}
}
}

View File

@ -199,9 +199,12 @@ public void should_remove_unpack_from_folder_name(string prefix)
[Test]
public void should_return_importresult_on_unknown_series()
{
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
.Returns(false);
var fileName = @"C:\folder\file.mkv".AsOsAgnostic();
var result = Subject.ProcessFile(new FileInfo(fileName));
var result = Subject.ProcessPath(fileName);
result.Should().HaveCount(1);
result.First().ImportDecision.Should().NotBeNull();

View File

@ -9,12 +9,13 @@
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Messaging.Events;
using NzbDrone.Core.Parser;
namespace NzbDrone.Core.Download
{
public interface ICompletedDownloadService
{
void Process(TrackedDownload trackedDownload);
void Process(TrackedDownload trackedDownload, bool ignoreWarnings = false);
}
public class CompletedDownloadService : ICompletedDownloadService
@ -23,46 +24,64 @@ public class CompletedDownloadService : ICompletedDownloadService
private readonly IEventAggregator _eventAggregator;
private readonly IHistoryService _historyService;
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
private readonly IParsingService _parsingService;
private readonly Logger _logger;
public CompletedDownloadService(IConfigService configService,
IEventAggregator eventAggregator,
IHistoryService historyService,
IDownloadedEpisodesImportService downloadedEpisodesImportService)
IDownloadedEpisodesImportService downloadedEpisodesImportService,
IParsingService parsingService,
Logger logger)
{
_configService = configService;
_eventAggregator = eventAggregator;
_historyService = historyService;
_downloadedEpisodesImportService = downloadedEpisodesImportService;
_parsingService = parsingService;
_logger = logger;
}
public void Process(TrackedDownload trackedDownload)
public void Process(TrackedDownload trackedDownload, bool ignoreWarnings = false)
{
if (trackedDownload.DownloadItem.Status != DownloadItemStatus.Completed)
{
return;
}
var historyItem = _historyService.MostRecentForDownloadId(trackedDownload.DownloadItem.DownloadId);
if (historyItem == null && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace())
if (!ignoreWarnings)
{
trackedDownload.Warn("Download wasn't grabbed by Sonarr and not in a category, Skipping.");
return;
}
var historyItem = _historyService.MostRecentForDownloadId(trackedDownload.DownloadItem.DownloadId);
var downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath;
if (historyItem == null && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace())
{
trackedDownload.Warn("Download wasn't grabbed by Sonarr and not in a category, Skipping.");
return;
}
if (downloadItemOutputPath.IsEmpty)
{
trackedDownload.Warn("Download doesn't contain intermediate path, Skipping.");
return;
}
var downloadItemOutputPath = trackedDownload.DownloadItem.OutputPath;
var downloadedEpisodesFolder = new OsPath(_configService.DownloadedEpisodesFolder);
if (downloadedEpisodesFolder.Contains(downloadItemOutputPath))
{
trackedDownload.Warn("Intermediate Download path inside drone factory, Skipping.");
return;
if (downloadItemOutputPath.IsEmpty)
{
trackedDownload.Warn("Download doesn't contain intermediate path, Skipping.");
return;
}
var downloadedEpisodesFolder = new OsPath(_configService.DownloadedEpisodesFolder);
if (downloadedEpisodesFolder.Contains(downloadItemOutputPath))
{
trackedDownload.Warn("Intermediate Download path inside drone factory, Skipping.");
return;
}
var series = _parsingService.GetSeries(trackedDownload.DownloadItem.Title);
if (series == null)
{
trackedDownload.Warn("Series title mismatch, automatic import is not possible.");
return;
}
}
Import(trackedDownload);
@ -71,7 +90,7 @@ public void Process(TrackedDownload trackedDownload)
private void Import(TrackedDownload trackedDownload)
{
var outputPath = trackedDownload.DownloadItem.OutputPath.FullPath;
var importResults = _downloadedEpisodesImportService.ProcessPath(outputPath, trackedDownload.DownloadItem);
var importResults = _downloadedEpisodesImportService.ProcessPath(outputPath, trackedDownload.RemoteEpisode.Series, trackedDownload.DownloadItem);
if (importResults.Empty())
{

View File

@ -78,7 +78,7 @@ private List<TrackedDownload> ProcessClientDownloads(IDownloadClient downloadCli
trackedDownloads.AddRange(newItems);
}
if (_configService.RemoveCompletedDownloads)
if (_configService.EnableCompletedDownloadHandling && _configService.RemoveCompletedDownloads)
{
RemoveCompletedDownloads(trackedDownloads);
}

View File

@ -1,6 +1,8 @@
using System;
using System.Linq;
using NLog;
using NzbDrone.Common.Cache;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.History;
using NzbDrone.Core.Parser;
@ -59,9 +61,17 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
if (parsedEpisodeInfo == null) return null;
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo);
if (remoteEpisode.Series == null)
{
return null;
var historyItems = _historyService.FindByDownloadId(downloadItem.DownloadId);
if (historyItems.Empty())
{
return null;
}
remoteEpisode = _parsingService.Map(parsedEpisodeInfo, historyItems.First().SeriesId, historyItems.Select(h => h.EpisodeId));
}
trackedDownload.RemoteEpisode = remoteEpisode;
@ -73,6 +83,7 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
}
var historyItem = _historyService.MostRecentForDownloadId(downloadItem.DownloadId);
if (historyItem != null)
{
trackedDownload.State = GetStateFromHistory(historyItem.EventType);

View File

@ -22,6 +22,7 @@ public interface IHistoryService
History MostRecentForDownloadId(string downloadId);
History Get(int historyId);
List<History> Find(string downloadId, HistoryEventType eventType);
List<History> FindByDownloadId(string downloadId);
}
public class HistoryService : IHistoryService,
@ -64,6 +65,10 @@ public List<History> Find(string downloadId, HistoryEventType eventType)
return _historyRepository.FindByDownloadId(downloadId).Where(c => c.EventType == eventType).ToList();
}
public List<History> FindByDownloadId(string downloadId)
{
return _historyRepository.FindByDownloadId(downloadId);
}
public QualityModel GetBestQualityInHistory(Profile profile, int episodeId)
{

View File

@ -15,8 +15,7 @@ namespace NzbDrone.Core.MediaFiles
public interface IDownloadedEpisodesImportService
{
List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo);
List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null);
List<ImportResult> ProcessPath(string path, DownloadClientItem downloadClientItem = null);
List<ImportResult> ProcessPath(string path, Series series = null, DownloadClientItem downloadClientItem = null);
}
public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
@ -31,13 +30,13 @@ public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
private readonly Logger _logger;
public DownloadedEpisodesImportService(IDiskProvider diskProvider,
IDiskScanService diskScanService,
ISeriesService seriesService,
IParsingService parsingService,
IMakeImportDecision importDecisionMaker,
IImportApprovedEpisodes importApprovedEpisodes,
ISampleService sampleService,
Logger logger)
IDiskScanService diskScanService,
ISeriesService seriesService,
IParsingService parsingService,
IMakeImportDecision importDecisionMaker,
IImportApprovedEpisodes importApprovedEpisodes,
ISampleService sampleService,
Logger logger)
{
_diskProvider = diskProvider;
_diskScanService = diskScanService;
@ -68,7 +67,27 @@ public List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo)
return results;
}
public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null)
public List<ImportResult> ProcessPath(string path, Series series = null, DownloadClientItem downloadClientItem = null)
{
if (_diskProvider.FolderExists(path))
{
if (series == null)
{
return ProcessFolder(new DirectoryInfo(path), downloadClientItem);
}
return ProcessFolder(new DirectoryInfo(path), series, downloadClientItem);
}
if (series == null)
{
return ProcessFile(new FileInfo(path), downloadClientItem);
}
return ProcessFile(new FileInfo(path), series, downloadClientItem);
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null)
{
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
var series = _parsingService.GetSeries(cleanedUpName);
@ -87,7 +106,7 @@ public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadCli
}
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series series,
DownloadClientItem downloadClientItem = null)
DownloadClientItem downloadClientItem = null)
{
if (_seriesService.SeriesPathExists(directoryInfo.FullName))
{
@ -128,7 +147,7 @@ private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, Series ser
return importResults;
}
public List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem = null)
private List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem = null)
{
var series = _parsingService.GetSeries(Path.GetFileNameWithoutExtension(fileInfo.Name));
@ -162,16 +181,6 @@ private List<ImportResult> ProcessFile(FileInfo fileInfo, Series series, Downloa
return _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
}
public List<ImportResult> ProcessPath(string path, DownloadClientItem downloadClientItem = null)
{
if (_diskProvider.FolderExists(path))
{
return ProcessFolder(new DirectoryInfo(path), downloadClientItem);
}
return ProcessFile(new FileInfo(path), downloadClientItem);
}
private string GetCleanedUpFolderName(string folder)
{
folder = folder.Replace("_UNPACK_", "")

View File

@ -16,6 +16,7 @@ public interface IParsingService
LocalEpisode GetLocalEpisode(string filename, Series series, bool sceneSource);
Series GetSeries(string title);
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 tvRageId = 0, SearchCriteriaBase searchCriteria = null);
RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 seriesId, IEnumerable<Int32> episodeIds);
List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null);
ParsedEpisodeInfo ParseSpecialEpisodeTitle(string title, int tvRageId, SearchCriteriaBase searchCriteria = null);
}
@ -118,6 +119,16 @@ public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, Int32 tvRageId, Se
return remoteEpisode;
}
public RemoteEpisode Map(ParsedEpisodeInfo parsedEpisodeInfo, int seriesId, IEnumerable<int> episodeIds)
{
return new RemoteEpisode
{
ParsedEpisodeInfo = parsedEpisodeInfo,
Series = _seriesService.GetSeries(seriesId),
Episodes = _episodeService.GetEpisodes(episodeIds)
};
}
public List<Episode> GetEpisodes(ParsedEpisodeInfo parsedEpisodeInfo, Series series, bool sceneSource, SearchCriteriaBase searchCriteria = null)
{
var result = new List<Episode>();