mirror of
https://github.com/Sonarr/Sonarr.git
synced 2024-12-16 11:37:58 +02:00
New: Improved the DownloadedEpisodesScanCommand endpoint to better support external triggers to CDH. (nzbToMedia)
This commit is contained in:
parent
ba38160430
commit
0b48630c00
@ -118,7 +118,7 @@ private void GivenCompletedImport()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>
|
||||
{
|
||||
new ImportResult(null)
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }))
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,173 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FizzWare.NBuilder;
|
||||
using FluentAssertions;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
using NzbDrone.Core.Test.Framework;
|
||||
using NzbDrone.Test.Common;
|
||||
|
||||
namespace NzbDrone.Core.Test.MediaFiles
|
||||
{
|
||||
[TestFixture]
|
||||
public class DownloadedEpisodesCommandServiceFixture : CoreTest<DownloadedEpisodesCommandService>
|
||||
{
|
||||
private string _droneFactory = "c:\\drop\\".AsOsAgnostic();
|
||||
private string _downloadFolder = "c:\\drop_other\\Show.S01E01\\".AsOsAgnostic();
|
||||
|
||||
private TrackedDownload _trackedDownload;
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IConfigService>().SetupGet(c => c.DownloadedEpisodesFolder)
|
||||
.Returns(_droneFactory);
|
||||
|
||||
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>());
|
||||
|
||||
var downloadItem = Builder<DownloadClientItem>.CreateNew()
|
||||
.With(v => v.DownloadClientId = "sab1")
|
||||
.With(v => v.Status = DownloadItemStatus.Downloading)
|
||||
.Build();
|
||||
|
||||
_trackedDownload = new TrackedDownload
|
||||
{
|
||||
DownloadItem = downloadItem,
|
||||
State = TrackedDownloadState.Downloading
|
||||
};
|
||||
}
|
||||
|
||||
private void GivenValidQueueItem()
|
||||
{
|
||||
var downloadItem = Builder<DownloadClientItem>.CreateNew()
|
||||
.With(v => v.DownloadClientId = "sab1")
|
||||
.With(v => v.Status = DownloadItemStatus.Downloading)
|
||||
.Build();
|
||||
|
||||
Mocker.GetMock<IDownloadTrackingService>()
|
||||
.Setup(s => s.GetQueuedDownloads())
|
||||
.Returns(new [] { _trackedDownload });
|
||||
}
|
||||
|
||||
private void GivenSuccessfulImport()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>() {
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }))
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenRejectedImport()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>() {
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }, "Some Rejection"), "I was rejected")
|
||||
});
|
||||
}
|
||||
|
||||
private void GivenSkippedImport()
|
||||
{
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>()
|
||||
.Setup(v => v.ProcessFolder(It.IsAny<DirectoryInfo>(), It.IsAny<DownloadClientItem>()))
|
||||
.Returns(new List<ImportResult>() {
|
||||
new ImportResult(new ImportDecision(new LocalEpisode() { Path = @"C:\TestPath\Droned.S01E01.mkv" }), "I was skipped")
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_dronefactory_if_path_is_not_specified()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessRootFolder(It.IsAny<DirectoryInfo>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_import_if_dropfolder_doesnt_exist()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>())).Returns(false);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessRootFolder(It.IsAny<DirectoryInfo>()), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_ignore_downloadclientid_if_path_is_not_specified()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { DownloadClientId = "sab1" });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessRootFolder(It.IsAny<DirectoryInfo>()), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_if_downloadclientid_is_not_specified()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessFolder(It.IsAny<DirectoryInfo>(), null), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_with_downloadclientitem_if_available()
|
||||
{
|
||||
GivenValidQueueItem();
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
Mocker.GetMock<ICompletedDownloadService>().Verify(c => c.Import(It.Is<TrackedDownload>(v => v != null), _downloadFolder), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_process_folder_without_downloadclientitem_if_not_available()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
Mocker.GetMock<IDownloadedEpisodesImportService>().Verify(c => c.ProcessFolder(It.IsAny<DirectoryInfo>(), null), Times.Once());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_not_mark_trackeddownload_as_completed_if_import_rejected()
|
||||
{
|
||||
GivenValidQueueItem();
|
||||
GivenRejectedImport();
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
_trackedDownload.State.Should().Be(TrackedDownloadState.Downloading);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_mark_trackeddownload_as_completed_if_import_skipped()
|
||||
{
|
||||
GivenValidQueueItem();
|
||||
GivenRejectedImport();
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand() { Path = _downloadFolder, DownloadClientId = "sab1" });
|
||||
|
||||
_trackedDownload.State.Should().Be(TrackedDownloadState.Imported);
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ namespace NzbDrone.Core.Test.MediaFiles
|
||||
[TestFixture]
|
||||
public class DownloadedEpisodesImportServiceFixture : CoreTest<DownloadedEpisodesImportService>
|
||||
{
|
||||
private string _droneFactory = "c:\\drop\\".AsOsAgnostic();
|
||||
private string[] _subFolders = new[] { "c:\\root\\foldername".AsOsAgnostic() };
|
||||
private string[] _videoFiles = new[] { "c:\\root\\foldername\\30.rock.s01e01.ext".AsOsAgnostic() };
|
||||
|
||||
@ -37,9 +38,6 @@ public void Setup()
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Mocker.GetMock<IConfigService>().SetupGet(c => c.DownloadedEpisodesFolder)
|
||||
.Returns("c:\\drop\\".AsOsAgnostic());
|
||||
|
||||
Mocker.GetMock<IImportApprovedEpisodes>()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
|
||||
.Returns(new List<ImportResult>());
|
||||
@ -55,25 +53,11 @@ private void GivenValidSeries()
|
||||
[Test]
|
||||
public void should_search_for_series_using_folder_name()
|
||||
{
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>().Verify(c => c.GetSeries("foldername"), Times.Once());
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_import_if_dropfolder_doesnt_exist()
|
||||
{
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.FolderExists(It.IsAny<string>())).Returns(false);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.GetDirectories(It.IsAny<string>()), Times.Never());
|
||||
Mocker.GetMock<IDiskProvider>().Verify(c => c.GetFiles(It.IsAny<string>(), It.IsAny<SearchOption>()), Times.Never());
|
||||
|
||||
ExceptionVerification.ExpectedWarns(1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void should_skip_if_file_is_in_use_by_another_process()
|
||||
{
|
||||
@ -82,7 +66,7 @@ public void should_skip_if_file_is_in_use_by_another_process()
|
||||
Mocker.GetMock<IDiskProvider>().Setup(c => c.IsFileLocked(It.IsAny<string>()))
|
||||
.Returns(true);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
VerifyNoImport();
|
||||
}
|
||||
@ -92,7 +76,7 @@ public void should_skip_if_no_series_found()
|
||||
{
|
||||
Mocker.GetMock<IParsingService>().Setup(c => c.GetSeries("foldername")).Returns((Series)null);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IMakeImportDecision>()
|
||||
.Verify(c => c.GetImportDecisions(It.IsAny<List<string>>(), It.IsAny<Series>(), It.IsAny<bool>(), It.IsAny<QualityModel>()),
|
||||
@ -112,7 +96,7 @@ public void should_not_import_if_folder_is_a_series_path()
|
||||
.Setup(c => c.GetVideoFiles(It.IsAny<string>(), It.IsAny<bool>()))
|
||||
.Returns(new string[0]);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(It.IsAny<String>()), Times.Never());
|
||||
@ -127,7 +111,7 @@ public void should_not_delete_folder_if_no_files_were_imported()
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), false, null))
|
||||
.Returns(new List<ImportResult>());
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.GetFolderSize(It.IsAny<String>()), Times.Never());
|
||||
@ -151,7 +135,7 @@ public void should_not_delete_folder_if_files_were_imported_and_video_files_rema
|
||||
.Setup(s => s.Import(It.IsAny<List<ImportDecision>>(), true, null))
|
||||
.Returns(imported.Select(i => new ImportResult(i)).ToList());
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<String>(), true), Times.Never());
|
||||
@ -185,7 +169,7 @@ public void should_delete_folder_if_files_were_imported_and_only_sample_files_re
|
||||
It.IsAny<Int32>()))
|
||||
.Returns(true);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IDiskProvider>()
|
||||
.Verify(v => v.DeleteFolder(It.IsAny<String>(), true), Times.Once());
|
||||
@ -202,7 +186,7 @@ public void should_remove_unpack_from_folder_name(string prefix)
|
||||
.Setup(c => c.GetDirectories(It.IsAny<string>()))
|
||||
.Returns(folders);
|
||||
|
||||
Subject.Execute(new DownloadedEpisodesScanCommand());
|
||||
Subject.ProcessRootFolder(new DirectoryInfo(_droneFactory));
|
||||
|
||||
Mocker.GetMock<IParsingService>()
|
||||
.Verify(v => v.GetSeries(folderName), Times.Once());
|
||||
|
@ -197,6 +197,7 @@
|
||||
<Compile Include="MediaCoverTests\CoverExistsSpecificationFixture.cs" />
|
||||
<Compile Include="MediaCoverTests\MediaCoverServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\DiskScanServiceTests\ScanFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesCommandServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportServiceFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingServiceTests\MoveEpisodeFileFixture.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeImport\ImportDecisionMakerFixture.cs" />
|
||||
|
@ -15,7 +15,7 @@ namespace NzbDrone.Core.Download
|
||||
public interface ICompletedDownloadService
|
||||
{
|
||||
void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownload trackedDownload, List<History.History> grabbedHistory, List<History.History> importedHistory);
|
||||
List<ImportResult> Import(TrackedDownload trackedDownload);
|
||||
List<ImportResult> Import(TrackedDownload trackedDownload, String overrideOutputPath = null);
|
||||
}
|
||||
|
||||
public class CompletedDownloadService : ICompletedDownloadService
|
||||
@ -110,21 +110,22 @@ public void CheckForCompletedItem(IDownloadClient downloadClient, TrackedDownloa
|
||||
}
|
||||
}
|
||||
|
||||
public List<ImportResult> Import(TrackedDownload trackedDownload)
|
||||
public List<ImportResult> Import(TrackedDownload trackedDownload, String overrideOutputPath = null)
|
||||
{
|
||||
var importResults = new List<ImportResult>();
|
||||
var outputPath = overrideOutputPath ?? trackedDownload.DownloadItem.OutputPath;
|
||||
|
||||
if (_diskProvider.FolderExists(trackedDownload.DownloadItem.OutputPath))
|
||||
if (_diskProvider.FolderExists(outputPath))
|
||||
{
|
||||
importResults = _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(trackedDownload.DownloadItem.OutputPath), trackedDownload.DownloadItem);
|
||||
importResults = _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(outputPath), trackedDownload.DownloadItem);
|
||||
|
||||
ProcessImportResults(trackedDownload, importResults);
|
||||
ProcessImportResults(trackedDownload, outputPath, importResults);
|
||||
}
|
||||
else if (_diskProvider.FileExists(trackedDownload.DownloadItem.OutputPath))
|
||||
else if (_diskProvider.FileExists(outputPath))
|
||||
{
|
||||
importResults = _downloadedEpisodesImportService.ProcessFile(new FileInfo(trackedDownload.DownloadItem.OutputPath), trackedDownload.DownloadItem);
|
||||
importResults = _downloadedEpisodesImportService.ProcessFile(new FileInfo(outputPath), trackedDownload.DownloadItem);
|
||||
|
||||
ProcessImportResults(trackedDownload, importResults);
|
||||
ProcessImportResults(trackedDownload, outputPath, importResults);
|
||||
}
|
||||
|
||||
return importResults;
|
||||
@ -147,11 +148,11 @@ private void UpdateStatusMessage(TrackedDownload trackedDownload, LogLevel logLe
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessImportResults(TrackedDownload trackedDownload, List<ImportResult> importResults)
|
||||
private void ProcessImportResults(TrackedDownload trackedDownload, String outputPath, List<ImportResult> importResults)
|
||||
{
|
||||
if (importResults.Empty())
|
||||
{
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Error, "No files found are eligible for import in {0}", trackedDownload.DownloadItem.OutputPath);
|
||||
UpdateStatusMessage(trackedDownload, LogLevel.Error, "No files found are eligible for import in {0}", outputPath);
|
||||
}
|
||||
else if (importResults.Any(v => v.Result == ImportResultType.Imported) && importResults.All(v => v.Result == ImportResultType.Imported || v.Result == ImportResultType.Rejected))
|
||||
{
|
||||
|
@ -15,5 +15,6 @@ public override bool SendUpdatesToClient
|
||||
|
||||
public Boolean SendUpdates { get; set; }
|
||||
public String Path { get; set; }
|
||||
public String DownloadClientId { get; set; }
|
||||
}
|
||||
}
|
110
src/NzbDrone.Core/MediaFiles/DownloadedEpisodesCommandService.cs
Normal file
110
src/NzbDrone.Core/MediaFiles/DownloadedEpisodesCommandService.cs
Normal file
@ -0,0 +1,110 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using NLog;
|
||||
using NzbDrone.Common;
|
||||
using NzbDrone.Common.Disk;
|
||||
using NzbDrone.Core.Configuration;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.MediaFiles.Commands;
|
||||
using NzbDrone.Core.MediaFiles.EpisodeImport;
|
||||
using NzbDrone.Core.Messaging.Commands;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public class DownloadedEpisodesCommandService : IExecute<DownloadedEpisodesScanCommand>
|
||||
{
|
||||
private readonly IDownloadedEpisodesImportService _downloadedEpisodesImportService;
|
||||
private readonly IDownloadTrackingService _downloadTrackingService;
|
||||
private readonly ICompletedDownloadService _completedDownloadService;
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly Logger _logger;
|
||||
|
||||
public DownloadedEpisodesCommandService(IDownloadedEpisodesImportService downloadedEpisodesImportService,
|
||||
IDownloadTrackingService downloadTrackingService,
|
||||
ICompletedDownloadService completedDownloadService,
|
||||
IDiskProvider diskProvider,
|
||||
IConfigService configService,
|
||||
Logger logger)
|
||||
{
|
||||
_downloadedEpisodesImportService = downloadedEpisodesImportService;
|
||||
_downloadTrackingService = downloadTrackingService;
|
||||
_completedDownloadService = completedDownloadService;
|
||||
_diskProvider = diskProvider;
|
||||
_configService = configService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
private List<ImportResult> ProcessDroneFactoryFolder()
|
||||
{
|
||||
var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
|
||||
|
||||
if (String.IsNullOrEmpty(downloadedEpisodesFolder))
|
||||
{
|
||||
_logger.Trace("Drone Factory folder is not configured");
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(downloadedEpisodesFolder))
|
||||
{
|
||||
_logger.Warn("Drone Factory folder [{0}] doesn't exist.", downloadedEpisodesFolder);
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
return _downloadedEpisodesImportService.ProcessRootFolder(new DirectoryInfo(downloadedEpisodesFolder));
|
||||
}
|
||||
|
||||
private List<ImportResult> ProcessFolder(DownloadedEpisodesScanCommand message)
|
||||
{
|
||||
if (!_diskProvider.FolderExists(message.Path))
|
||||
{
|
||||
_logger.Warn("Folder specified for import scan [{0}] doesn't exist.", message.Path);
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
if (message.DownloadClientId.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
var trackedDownload = _downloadTrackingService.GetQueuedDownloads().Where(v => v.DownloadItem.DownloadClientId == message.DownloadClientId).FirstOrDefault();
|
||||
|
||||
if (trackedDownload == null)
|
||||
{
|
||||
_logger.Warn("External directory scan request for unknown download {0}, attempting normal import. [{1}]", message.DownloadClientId, message.Path);
|
||||
|
||||
return _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(message.Path));
|
||||
}
|
||||
else
|
||||
{
|
||||
return _completedDownloadService.Import(trackedDownload, message.Path);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return _downloadedEpisodesImportService.ProcessFolder(new DirectoryInfo(message.Path));
|
||||
}
|
||||
}
|
||||
|
||||
public void Execute(DownloadedEpisodesScanCommand message)
|
||||
{
|
||||
List<ImportResult> importResults;
|
||||
|
||||
if (message.Path.IsNotNullOrWhiteSpace())
|
||||
{
|
||||
importResults = ProcessFolder(message);
|
||||
}
|
||||
else
|
||||
{
|
||||
importResults = ProcessDroneFactoryFolder();
|
||||
}
|
||||
|
||||
if (importResults == null || !importResults.Any(v => v.Result == ImportResultType.Imported))
|
||||
{
|
||||
// Atm we don't report it as a command failure, coz that would cause the download to be failed.
|
||||
// Changing the message won't do a thing either, coz it will get set to 'Completed' a msec later.
|
||||
//message.SetMessage("Failed to import");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,16 +13,18 @@
|
||||
using NzbDrone.Core.Parser;
|
||||
using NzbDrone.Core.Tv;
|
||||
using NzbDrone.Core.Download;
|
||||
using NzbDrone.Core.Parser.Model;
|
||||
|
||||
namespace NzbDrone.Core.MediaFiles
|
||||
{
|
||||
public interface IDownloadedEpisodesImportService
|
||||
{
|
||||
List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem);
|
||||
List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem);
|
||||
List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo);
|
||||
List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null);
|
||||
List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem = null);
|
||||
}
|
||||
|
||||
public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService, IExecute<DownloadedEpisodesScanCommand>
|
||||
public class DownloadedEpisodesImportService : IDownloadedEpisodesImportService
|
||||
{
|
||||
private readonly IDiskProvider _diskProvider;
|
||||
private readonly IDiskScanService _diskScanService;
|
||||
@ -55,8 +57,33 @@ public DownloadedEpisodesImportService(IDiskProvider diskProvider,
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem)
|
||||
public List<ImportResult> ProcessRootFolder(DirectoryInfo directoryInfo)
|
||||
{
|
||||
var results = new List<ImportResult>();
|
||||
|
||||
foreach (var subFolder in _diskProvider.GetDirectories(directoryInfo.FullName))
|
||||
{
|
||||
var folderResults = ProcessFolder(new DirectoryInfo(subFolder));
|
||||
results.AddRange(folderResults);
|
||||
}
|
||||
|
||||
foreach (var videoFile in _diskScanService.GetVideoFiles(directoryInfo.FullName, false))
|
||||
{
|
||||
var fileResults = ProcessFile(new FileInfo(videoFile));
|
||||
results.AddRange(fileResults);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadClientItem downloadClientItem = null)
|
||||
{
|
||||
if (_seriesService.SeriesPathExists(directoryInfo.FullName))
|
||||
{
|
||||
_logger.Warn("Unable to process folder that contains sorted TV Shows");
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var series = _parsingService.GetSeries(cleanedUpName);
|
||||
var quality = QualityParser.ParseQuality(cleanedUpName);
|
||||
@ -65,82 +92,10 @@ public List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo, DownloadCli
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series {0}", cleanedUpName);
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality);
|
||||
|
||||
var importResults = _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
|
||||
if (!downloadClientItem.IsReadOnly && importResults.Any() && ShouldDeleteFolder(directoryInfo))
|
||||
{
|
||||
_logger.Debug("Deleting folder after importing valid files");
|
||||
_diskProvider.DeleteFolder(directoryInfo.FullName, true);
|
||||
}
|
||||
|
||||
return importResults;
|
||||
}
|
||||
|
||||
public List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem)
|
||||
{
|
||||
var series = _parsingService.GetSeries(Path.GetFileNameWithoutExtension(fileInfo.Name));
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series for file: {0}", fileInfo.Name);
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, series, true);
|
||||
return _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
}
|
||||
|
||||
private void ProcessDownloadedEpisodesFolder()
|
||||
{
|
||||
var downloadedEpisodesFolder = _configService.DownloadedEpisodesFolder;
|
||||
|
||||
if (String.IsNullOrEmpty(downloadedEpisodesFolder))
|
||||
{
|
||||
_logger.Trace("Drone Factory folder is not configured");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_diskProvider.FolderExists(downloadedEpisodesFolder))
|
||||
{
|
||||
_logger.Warn("Drone Factory folder [{0}] doesn't exist.", downloadedEpisodesFolder);
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var subFolder in _diskProvider.GetDirectories(downloadedEpisodesFolder))
|
||||
{
|
||||
ProcessFolder(subFolder);
|
||||
}
|
||||
|
||||
foreach (var videoFile in _diskScanService.GetVideoFiles(downloadedEpisodesFolder, false))
|
||||
{
|
||||
try
|
||||
{
|
||||
ProcessVideoFile(videoFile);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.ErrorException("An error has occurred while importing video file" + videoFile, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo)
|
||||
{
|
||||
var cleanedUpName = GetCleanedUpFolderName(directoryInfo.Name);
|
||||
var series = _parsingService.GetSeries(cleanedUpName);
|
||||
var quality = QualityParser.ParseQuality(cleanedUpName);
|
||||
_logger.Debug("{0} folder quality: {1}", cleanedUpName, quality);
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series {0}", cleanedUpName);
|
||||
return new List<ImportResult>();
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(null, "Unknown Series"), "Unknown Series")
|
||||
};
|
||||
}
|
||||
|
||||
var videoFiles = _diskScanService.GetVideoFiles(directoryInfo.FullName);
|
||||
@ -150,59 +105,44 @@ private List<ImportResult> ProcessFolder(DirectoryInfo directoryInfo)
|
||||
if (_diskProvider.IsFileLocked(videoFile))
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
|
||||
return new List<ImportResult>();
|
||||
return new List<ImportResult>
|
||||
{
|
||||
new ImportResult(new ImportDecision(new LocalEpisode { Path = videoFile }, "Locked file, try again later"), "Locked file, try again later")
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(videoFiles.ToList(), series, true, quality);
|
||||
return _importApprovedEpisodes.Import(decisions, true);
|
||||
|
||||
var importResults = _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
|
||||
if ((downloadClientItem == null || !downloadClientItem.IsReadOnly) && importResults.Any() && ShouldDeleteFolder(directoryInfo))
|
||||
{
|
||||
_logger.Debug("Deleting folder after importing valid files");
|
||||
_diskProvider.DeleteFolder(directoryInfo.FullName, true);
|
||||
}
|
||||
|
||||
return importResults;
|
||||
}
|
||||
|
||||
private void ProcessVideoFile(string videoFile)
|
||||
public List<ImportResult> ProcessFile(FileInfo fileInfo, DownloadClientItem downloadClientItem = null)
|
||||
{
|
||||
var series = _parsingService.GetSeries(Path.GetFileNameWithoutExtension(videoFile));
|
||||
var series = _parsingService.GetSeries(Path.GetFileNameWithoutExtension(fileInfo.Name));
|
||||
|
||||
if (series == null)
|
||||
{
|
||||
_logger.Debug("Unknown Series for file: {0}", videoFile);
|
||||
return;
|
||||
_logger.Debug("Unknown Series for file: {0}", fileInfo.Name);
|
||||
return new List<ImportResult>() { new ImportResult(null, String.Format("Unknown Series for file: {0}", fileInfo.Name)) };
|
||||
}
|
||||
|
||||
if (_diskProvider.IsFileLocked(videoFile))
|
||||
if (_diskProvider.IsFileLocked(fileInfo.FullName))
|
||||
{
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", videoFile);
|
||||
return;
|
||||
_logger.Debug("[{0}] is currently locked by another process, skipping", fileInfo.FullName);
|
||||
return new List<ImportResult>();
|
||||
}
|
||||
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new [] { videoFile }.ToList(), series, true, null);
|
||||
_importApprovedEpisodes.Import(decisions, true);
|
||||
}
|
||||
|
||||
private void ProcessFolder(string path)
|
||||
{
|
||||
Ensure.That(path, () => path).IsValidPath();
|
||||
|
||||
try
|
||||
{
|
||||
if (_seriesService.SeriesPathExists(path))
|
||||
{
|
||||
_logger.Warn("Unable to process folder that contains sorted TV Shows");
|
||||
return;
|
||||
}
|
||||
|
||||
var directoryFolderInfo = new DirectoryInfo(path);
|
||||
var importedFiles = ProcessFolder(directoryFolderInfo);
|
||||
|
||||
if (importedFiles.Any() && ShouldDeleteFolder(directoryFolderInfo))
|
||||
{
|
||||
_logger.Debug("Deleting folder after importing valid files");
|
||||
_diskProvider.DeleteFolder(path, true);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.ErrorException("An error has occurred while importing folder: " + path, e);
|
||||
}
|
||||
var decisions = _importDecisionMaker.GetImportDecisions(new List<string>() { fileInfo.FullName }, series, true);
|
||||
return _importApprovedEpisodes.Import(decisions, true, downloadClientItem);
|
||||
}
|
||||
|
||||
private string GetCleanedUpFolderName(string folder)
|
||||
@ -242,18 +182,5 @@ private bool ShouldDeleteFolder(DirectoryInfo directoryInfo)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void Execute(DownloadedEpisodesScanCommand message)
|
||||
{
|
||||
if (message.Path.IsNullOrWhiteSpace())
|
||||
{
|
||||
ProcessDownloadedEpisodesFolder();
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
ProcessFolder(message.Path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -481,6 +481,7 @@
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesImportService.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Include="MediaFiles\DownloadedEpisodesCommandService.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFile.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMoveResult.cs" />
|
||||
<Compile Include="MediaFiles\EpisodeFileMovingService.cs" />
|
||||
|
Loading…
Reference in New Issue
Block a user