diff --git a/NzbDrone.Common/DiskProvider.cs b/NzbDrone.Common/DiskProvider.cs index 77920b144..f207af40b 100644 --- a/NzbDrone.Common/DiskProvider.cs +++ b/NzbDrone.Common/DiskProvider.cs @@ -232,5 +232,41 @@ namespace NzbDrone.Common { Directory.SetLastWriteTimeUtc(path, dateTime); } + + public virtual bool IsFolderLocked(string path) + { + var files = GetFileInfos(path, "*.*", SearchOption.AllDirectories); + + foreach(var fileInfo in files) + { + if (IsFileLocked(fileInfo)) + return true; + } + + return false; + } + + public virtual bool IsFileLocked(FileInfo file) + { + FileStream stream = null; + + try + { + stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None); + } + catch (IOException) + { + return true; + } + finally + { + if (stream != null) + stream.Close(); + } + + //file is not locked + return false; + } + } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs index b538a3576..de0ad4597 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessDownloadFixture.cs @@ -300,6 +300,7 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests Mocker.GetMock().Setup(s => s.GetDirectorySize(droppedFolder.FullName)).Returns(Constants.IgnoreFileSize - 1.Megabytes()); Mocker.GetMock().Setup(s => s.DeleteFolder(droppedFolder.FullName, true)); Mocker.GetMock().Setup(s => s.FolderExists(fakeSeries.Path)).Returns(true); + Mocker.GetMock().Setup(s => s.IsFolderLocked(droppedFolder.FullName)).Returns(false); Mocker.GetMock().Setup(s => s.CreateForEpisodeFiles(It.IsAny>())); //Act @@ -434,5 +435,21 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests Mocker.GetMock().Verify(c => c.GetDirectorySize(It.IsAny()), Times.Never()); ExceptionVerification.ExpectedWarns(1); } + + [Test] + public void should_skip_if_folder_is_in_use_by_another_process() + { + var downloadName = new DirectoryInfo(@"C:\Test\Drop\30.Rock.S01E01.Pilot"); + + WithValidSeries(); + + Mocker.GetMock() + .Setup(s => s.IsFolderLocked(downloadName.FullName)) + .Returns(true); + + Mocker.Resolve().ProcessDownload(downloadName); + + Mocker.GetMock().Verify(c => c.GetDirectorySize(It.IsAny()), Times.Never()); + } } } \ No newline at end of file diff --git a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs index d4413aa36..2f5535f93 100644 --- a/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs +++ b/NzbDrone.Core.Test/ProviderTests/PostDownloadProviderTests/ProcessVideoFileFixture.cs @@ -227,14 +227,6 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests WithValidSeries(); - Mocker.GetMock() - .Setup(s => s.GetDirectorySize(downloadName)) - .Returns(10); - - Mocker.GetMock() - .Setup(s => s.FreeDiskSpace(It.IsAny())) - .Returns(10); - Mocker.GetMock() .Setup(s => s.FolderExists(fakeSeries.Path)) .Returns(false); @@ -246,5 +238,24 @@ namespace NzbDrone.Core.Test.ProviderTests.PostDownloadProviderTests //Assert ExceptionVerification.ExpectedWarns(1); } + + [Test] + public void should_skip_if_file_is_in_use_by_another_process() + { + var downloadName = @"C:\Test\Drop\30.Rock.S01E01.Pilot.mkv"; + + WithValidSeries(); + + Mocker.GetMock() + .Setup(s => s.IsFileLocked(It.Is(f => f.FullName == downloadName))) + .Returns(true); + + //Act + Mocker.Resolve().ProcessVideoFile(downloadName); + + + //Assert + Mocker.GetMock().Verify(c => c.ImportFile(fakeSeries, downloadName), Times.Never()); + } } } \ No newline at end of file diff --git a/NzbDrone.Core/Providers/PostDownloadProvider.cs b/NzbDrone.Core/Providers/PostDownloadProvider.cs index e0e4ef874..22208e732 100644 --- a/NzbDrone.Core/Providers/PostDownloadProvider.cs +++ b/NzbDrone.Core/Providers/PostDownloadProvider.cs @@ -71,6 +71,12 @@ namespace NzbDrone.Core.Providers return; } + if (_diskProvider.IsFolderLocked(subfolderInfo.FullName)) + { + Logger.Trace("[{0}] is currently locked by another process, skipping", subfolderInfo.Name); + return; + } + string seriesName = Parser.ParseSeriesName(RemoveStatusFromFolderName(subfolderInfo.Name)); var series = _seriesProvider.FindSeries(seriesName); @@ -135,6 +141,12 @@ namespace NzbDrone.Core.Providers return; } + if (_diskProvider.IsFileLocked(new FileInfo(videoFile))) + { + Logger.Trace("[{0}] is currently locked by another process, skipping", videoFile); + return; + } + var seriesName = Parser.ParseSeriesName(Path.GetFileNameWithoutExtension(videoFile)); var series = _seriesProvider.FindSeries(seriesName);