mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-04 10:02:40 +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:
parent
7f27507ef6
commit
2bbce39faa
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
@ -66,17 +67,23 @@ private void GivenNoGrabbedHistory()
|
||||
.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 @@ public void should_process_if_matching_history_is_not_found_but_category_specifi
|
||||
{
|
||||
_trackedDownload.DownloadItem.Category = "tv";
|
||||
GivenNoGrabbedHistory();
|
||||
GivenSeriesMatch();
|
||||
GivenSuccessfulImport();
|
||||
|
||||
Subject.Process(_trackedDownload);
|
||||
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
@ -58,6 +52,5 @@ public void should_skip_import_if_dropfolder_doesnt_exist()
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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,25 +24,33 @@ 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;
|
||||
}
|
||||
|
||||
if (!ignoreWarnings)
|
||||
{
|
||||
var historyItem = _historyService.MostRecentForDownloadId(trackedDownload.DownloadItem.DownloadId);
|
||||
|
||||
if (historyItem == null && trackedDownload.DownloadItem.Category.IsNullOrWhiteSpace())
|
||||
@ -59,19 +68,29 @@ public void Process(TrackedDownload trackedDownload)
|
||||
}
|
||||
|
||||
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())
|
||||
{
|
||||
|
@ -78,7 +78,7 @@ private List<TrackedDownload> ProcessClientDownloads(IDownloadClient downloadCli
|
||||
trackedDownloads.AddRange(newItems);
|
||||
}
|
||||
|
||||
if (_configService.RemoveCompletedDownloads)
|
||||
if (_configService.EnableCompletedDownloadHandling && _configService.RemoveCompletedDownloads)
|
||||
{
|
||||
RemoveCompletedDownloads(trackedDownloads);
|
||||
}
|
||||
|
@ -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 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
|
||||
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 @@ public TrackedDownload TrackDownload(DownloadClientDefinition downloadClient, Do
|
||||
}
|
||||
|
||||
var historyItem = _historyService.MostRecentForDownloadId(downloadItem.DownloadId);
|
||||
|
||||
if (historyItem != null)
|
||||
{
|
||||
trackedDownload.State = GetStateFromHistory(historyItem.EventType);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 @@ 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);
|
||||
@ -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_", "")
|
||||
|
@ -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>();
|
||||
|
Loading…
Reference in New Issue
Block a user