New: Check for available space before grabbing

(cherry picked from commit 4b5ff3927d3c123f9e3a2bc74328323fab1b0745)

Closes #5095
pull/5106/head
Mark McDowall 2 months ago committed by Bogdan
parent 856ac2ffa5
commit 5947b4642c

@ -191,26 +191,21 @@ class MediaManagement extends Component {
<FieldSet <FieldSet
legend={translate('Importing')} legend={translate('Importing')}
> >
{
!isWindows &&
<FormGroup <FormGroup
advancedSettings={advancedSettings} advancedSettings={advancedSettings}
isAdvanced={true} isAdvanced={true}
size={sizes.MEDIUM} size={sizes.MEDIUM}
> >
<FormLabel> <FormLabel>{translate('SkipFreeSpaceCheck')}</FormLabel>
{translate('SkipFreeSpaceCheck')}
</FormLabel>
<FormInputGroup <FormInputGroup
type={inputTypes.CHECK} type={inputTypes.CHECK}
name="skipFreeSpaceCheckWhenImporting" name="skipFreeSpaceCheckWhenImporting"
helpText={translate('SkipFreeSpaceCheckWhenImportingHelpText')} helpText={translate('SkipFreeSpaceCheckHelpText')}
onChange={onInputChange} onChange={onInputChange}
{...settings.skipFreeSpaceCheckWhenImporting} {...settings.skipFreeSpaceCheckWhenImporting}
/> />
</FormGroup> </FormGroup>
}
<FormGroup <FormGroup
advancedSettings={advancedSettings} advancedSettings={advancedSettings}

@ -0,0 +1,95 @@
using FluentAssertions;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.DecisionEngine.Specifications;
using NzbDrone.Core.Music;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.DecisionEngineTests
{
[TestFixture]
public class FreeSpaceSpecificationFixture : CoreTest<FreeSpaceSpecification>
{
private RemoteAlbum _remoteAlbum;
[SetUp]
public void Setup()
{
_remoteAlbum = new RemoteAlbum() { Release = new ReleaseInfo(), Artist = new Artist { Path = @"C:\Test\Music\Artist".AsOsAgnostic() } };
}
private void WithMinimumFreeSpace(int size)
{
Mocker.GetMock<IConfigService>().SetupGet(c => c.MinimumFreeSpaceWhenImporting).Returns(size);
}
private void WithAvailableSpace(int size)
{
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetAvailableSpace(It.IsAny<string>())).Returns(size.Megabytes());
}
private void WithSize(int size)
{
_remoteAlbum.Release.Size = size.Megabytes();
}
[Test]
public void should_return_true_when_available_space_is_more_than_size()
{
WithMinimumFreeSpace(0);
WithAvailableSpace(200);
WithSize(100);
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_true_when_available_space_minus_size_is_more_than_minimum_free_space()
{
WithMinimumFreeSpace(50);
WithAvailableSpace(200);
WithSize(100);
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
}
[Test]
public void should_return_false_available_space_is_less_than_size()
{
WithMinimumFreeSpace(0);
WithAvailableSpace(200);
WithSize(1000);
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_false_when_available_space_minus_size_is_less_than_minimum_free_space()
{
WithMinimumFreeSpace(150);
WithAvailableSpace(200);
WithSize(100);
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeFalse();
}
[Test]
public void should_return_true_if_skip_free_space_check_is_true()
{
Mocker.GetMock<IConfigService>()
.Setup(s => s.SkipFreeSpaceCheckWhenImporting)
.Returns(true);
WithMinimumFreeSpace(150);
WithAvailableSpace(200);
WithSize(100);
Subject.IsSatisfiedBy(_remoteAlbum, null).Accepted.Should().BeTrue();
}
}
}

@ -185,6 +185,7 @@ namespace NzbDrone.Core.Configuration
set { SetValue("DownloadClientHistoryLimit", value); } set { SetValue("DownloadClientHistoryLimit", value); }
} }
// TODO: Rename to 'Skip Free Space Check'
public bool SkipFreeSpaceCheckWhenImporting public bool SkipFreeSpaceCheckWhenImporting
{ {
get { return GetValueBoolean("SkipFreeSpaceCheckWhenImporting", false); } get { return GetValueBoolean("SkipFreeSpaceCheckWhenImporting", false); }

@ -0,0 +1,66 @@
using NLog;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.IndexerSearch.Definitions;
using NzbDrone.Core.Parser.Model;
namespace NzbDrone.Core.DecisionEngine.Specifications
{
public class FreeSpaceSpecification : IDecisionEngineSpecification
{
private readonly IConfigService _configService;
private readonly IDiskProvider _diskProvider;
private readonly Logger _logger;
public FreeSpaceSpecification(IConfigService configService, IDiskProvider diskProvider, Logger logger)
{
_configService = configService;
_diskProvider = diskProvider;
_logger = logger;
}
public SpecificationPriority Priority => SpecificationPriority.Default;
public RejectionType Type => RejectionType.Permanent;
public Decision IsSatisfiedBy(RemoteAlbum subject, SearchCriteriaBase searchCriteria)
{
if (_configService.SkipFreeSpaceCheckWhenImporting)
{
_logger.Debug("Skipping free space check");
return Decision.Accept();
}
var size = subject.Release.Size;
var freeSpace = _diskProvider.GetAvailableSpace(subject.Artist.Path);
if (!freeSpace.HasValue)
{
_logger.Debug("Unable to get available space for {0}. Skipping", subject.Artist.Path);
return Decision.Accept();
}
var minimumSpace = _configService.MinimumFreeSpaceWhenImporting.Megabytes();
var remainingSpace = freeSpace.Value - size;
if (remainingSpace <= 0)
{
var message = "Importing after download will exceed available disk space";
_logger.Debug(message);
return Decision.Reject(message);
}
if (remainingSpace < minimumSpace)
{
var message = $"Not enough free space ({minimumSpace.SizeSuffix()}) to import after download: {remainingSpace.SizeSuffix()}. (Settings: Media Management: Minimum Free Space)";
_logger.Debug(message);
return Decision.Reject(message);
}
return Decision.Accept();
}
}
}

@ -1127,7 +1127,7 @@
"SizeLimit": "Size Limit", "SizeLimit": "Size Limit",
"SizeOnDisk": "Size on Disk", "SizeOnDisk": "Size on Disk",
"SkipFreeSpaceCheck": "Skip Free Space Check", "SkipFreeSpaceCheck": "Skip Free Space Check",
"SkipFreeSpaceCheckWhenImportingHelpText": "Use when {appName} is unable to detect free space of your root folder during file import", "SkipFreeSpaceCheckHelpText": "Use when {appName} is unable to detect free space of your root folder",
"SkipRedownload": "Skip Redownload", "SkipRedownload": "Skip Redownload",
"SkipRedownloadHelpText": "Prevents {appName} from trying download alternative releases for the removed items", "SkipRedownloadHelpText": "Prevents {appName} from trying download alternative releases for the removed items",
"Small": "Small", "Small": "Small",

Loading…
Cancel
Save