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 @@ namespace NzbDrone.Api.Queue
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.History;
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;
@ -66,17 +67,23 @@ namespace NzbDrone.Core.Test.Download
.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,6 +114,7 @@ namespace NzbDrone.Core.Test.Download
{
_trackedDownload.DownloadItem.Category = "tv";
GivenNoGrabbedHistory();
GivenSeriesMatch();
GivenSuccessfulImport();
Subject.Process(_trackedDownload);
@ -142,7 +150,7 @@ namespace NzbDrone.Core.Test.Download
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 @@ namespace NzbDrone.Core.Test.Download
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 @@ namespace NzbDrone.Core.Test.Download
[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 @@ namespace NzbDrone.Core.Test.Download
};
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 @@ namespace NzbDrone.Core.Test.Download
};
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 @@ namespace NzbDrone.Core.Test.Download
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 @@ namespace NzbDrone.Core.Test.Download
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 @@ namespace NzbDrone.Core.Test.MediaFiles
{
private string _droneFactory = "c:\\drop\\".AsOsAgnostic();
[SetUp]
public void Setup()
{
@ -32,11 +31,6 @@ namespace NzbDrone.Core.Test.MediaFiles
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 @@ namespace NzbDrone.Core.Test.MediaFiles
}
[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);
@ -58,6 +52,5 @@ namespace NzbDrone.Core.Test.MediaFiles
ExceptionVerification.ExpectedWarns(1);
}
}
}

View File

@ -199,9 +199,12 @@ namespace NzbDrone.Core.Test.MediaFiles
[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.History;
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,25 +24,33 @@ namespace NzbDrone.Core.Download
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;
}
if (!ignoreWarnings)
{
var historyItem = _historyService.MostRecentForDownloadId(trackedDownload.DownloadItem.DownloadId);
if (historyItem == null && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace())
@ -59,19 +68,29 @@ namespace NzbDrone.Core.Download
}
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);
}
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 @@ namespace NzbDrone.Core.Download.TrackedDownloads
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,11 +61,19 @@ namespace NzbDrone.Core.Download.TrackedDownloads
if (parsedEpisodeInfo == null) return null;
var remoteEpisode = _parsingService.Map(parsedEpisodeInfo);
if (remoteEpisode.Series == 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;
}
catch (Exception e)
@ -73,6 +83,7 @@ namespace NzbDrone.Core.Download.TrackedDownloads
}
var historyItem = _historyService.MostRecentForDownloadId(downloadItem.DownloadId);
if (historyItem != null)
{
trackedDownload.State = GetStateFromHistory(historyItem.EventType);

View File

@ -22,6 +22,7 @@ namespace NzbDrone.Core.History
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 @@ namespace NzbDrone.Core.History
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
@ -68,7 +67,27 @@ namespace NzbDrone.Core.MediaFiles
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);
@ -128,7 +147,7 @@ namespace NzbDrone.Core.MediaFiles
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 @@ namespace NzbDrone.Core.MediaFiles
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 @@ namespace NzbDrone.Core.Parser
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 @@ namespace NzbDrone.Core.Parser
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>();