mirror of
https://github.com/Radarr/Radarr.git
synced 2024-11-20 01:42:35 +01:00
Cache root folders and improve getting disk space for movie path roots
(cherry picked from commit 63fdf8ca8ff9b22ce4cf8764cc05aad5d1d0ae62) Closes #10219
This commit is contained in:
parent
c9f28fdc4f
commit
401e19547c
@ -6,6 +6,7 @@
|
|||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Core.DiskSpace;
|
using NzbDrone.Core.DiskSpace;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.RootFolders;
|
||||||
using NzbDrone.Core.Test.Framework;
|
using NzbDrone.Core.Test.Framework;
|
||||||
using NzbDrone.Test.Common;
|
using NzbDrone.Test.Common;
|
||||||
|
|
||||||
@ -16,14 +17,14 @@ public class DiskSpaceServiceFixture : CoreTest<DiskSpaceService>
|
|||||||
{
|
{
|
||||||
private string _moviesFolder;
|
private string _moviesFolder;
|
||||||
private string _moviesFolder2;
|
private string _moviesFolder2;
|
||||||
private string _droneFactoryFolder;
|
private string _rootFolder;
|
||||||
|
|
||||||
[SetUp]
|
[SetUp]
|
||||||
public void SetUp()
|
public void SetUp()
|
||||||
{
|
{
|
||||||
_moviesFolder = @"G:\fasdlfsdf\movies".AsOsAgnostic();
|
_moviesFolder = @"G:\fasdlfsdf\movies".AsOsAgnostic();
|
||||||
_moviesFolder2 = @"G:\fasdlfsdf\movies2".AsOsAgnostic();
|
_moviesFolder2 = @"G:\fasdlfsdf\movies2".AsOsAgnostic();
|
||||||
_droneFactoryFolder = @"G:\dronefactory".AsOsAgnostic();
|
_rootFolder = @"G:\fasdlfsdf".AsOsAgnostic();
|
||||||
|
|
||||||
Mocker.GetMock<IDiskProvider>()
|
Mocker.GetMock<IDiskProvider>()
|
||||||
.Setup(v => v.GetMounts())
|
.Setup(v => v.GetMounts())
|
||||||
@ -51,6 +52,13 @@ private void GivenMovies(params Movie[] movies)
|
|||||||
.Returns(movies.ToDictionary(x => x.Id, x => x.Path));
|
.Returns(movies.ToDictionary(x => x.Id, x => x.Path));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void GivenRootFolder(string moviePath, string rootFolderPath)
|
||||||
|
{
|
||||||
|
Mocker.GetMock<IRootFolderService>()
|
||||||
|
.Setup(v => v.GetBestRootFolderPath(moviePath, null))
|
||||||
|
.Returns(rootFolderPath);
|
||||||
|
}
|
||||||
|
|
||||||
private void GivenExistingFolder(string folder)
|
private void GivenExistingFolder(string folder)
|
||||||
{
|
{
|
||||||
Mocker.GetMock<IDiskProvider>()
|
Mocker.GetMock<IDiskProvider>()
|
||||||
@ -62,8 +70,8 @@ private void GivenExistingFolder(string folder)
|
|||||||
public void should_check_diskspace_for_movies_folders()
|
public void should_check_diskspace_for_movies_folders()
|
||||||
{
|
{
|
||||||
GivenMovies(new Movie { Path = _moviesFolder });
|
GivenMovies(new Movie { Path = _moviesFolder });
|
||||||
|
GivenRootFolder(_moviesFolder, _rootFolder);
|
||||||
GivenExistingFolder(_moviesFolder);
|
GivenExistingFolder(_rootFolder);
|
||||||
|
|
||||||
var freeSpace = Subject.GetFreeSpace();
|
var freeSpace = Subject.GetFreeSpace();
|
||||||
|
|
||||||
@ -74,9 +82,9 @@ public void should_check_diskspace_for_movies_folders()
|
|||||||
public void should_check_diskspace_for_same_root_folder_only_once()
|
public void should_check_diskspace_for_same_root_folder_only_once()
|
||||||
{
|
{
|
||||||
GivenMovies(new Movie { Id = 1, Path = _moviesFolder }, new Movie { Id = 2, Path = _moviesFolder2 });
|
GivenMovies(new Movie { Id = 1, Path = _moviesFolder }, new Movie { Id = 2, Path = _moviesFolder2 });
|
||||||
|
GivenRootFolder(_moviesFolder, _rootFolder);
|
||||||
GivenExistingFolder(_moviesFolder);
|
GivenRootFolder(_moviesFolder, _rootFolder);
|
||||||
GivenExistingFolder(_moviesFolder2);
|
GivenExistingFolder(_rootFolder);
|
||||||
|
|
||||||
var freeSpace = Subject.GetFreeSpace();
|
var freeSpace = Subject.GetFreeSpace();
|
||||||
|
|
||||||
@ -87,9 +95,10 @@ public void should_check_diskspace_for_same_root_folder_only_once()
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void should_not_check_diskspace_for_missing_movie_folders()
|
public void should_not_check_diskspace_for_missing_movie_root_folders()
|
||||||
{
|
{
|
||||||
GivenMovies(new Movie { Path = _moviesFolder });
|
GivenMovies(new Movie { Path = _moviesFolder });
|
||||||
|
GivenRootFolder(_moviesFolder, _rootFolder);
|
||||||
|
|
||||||
var freeSpace = Subject.GetFreeSpace();
|
var freeSpace = Subject.GetFreeSpace();
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Movies;
|
using NzbDrone.Core.Movies;
|
||||||
|
using NzbDrone.Core.RootFolders;
|
||||||
|
|
||||||
namespace NzbDrone.Core.DiskSpace
|
namespace NzbDrone.Core.DiskSpace
|
||||||
{
|
{
|
||||||
@ -18,14 +19,16 @@ public interface IDiskSpaceService
|
|||||||
public class DiskSpaceService : IDiskSpaceService
|
public class DiskSpaceService : IDiskSpaceService
|
||||||
{
|
{
|
||||||
private readonly IMovieService _movieService;
|
private readonly IMovieService _movieService;
|
||||||
|
private readonly IRootFolderService _rootFolderService;
|
||||||
private readonly IDiskProvider _diskProvider;
|
private readonly IDiskProvider _diskProvider;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
|
private static readonly Regex _regexSpecialDrive = new Regex("^/var/lib/(docker|rancher|kubelet)(/|$)|^/(boot|etc)(/|$)|/docker(/var)?/aufs(/|$)", RegexOptions.Compiled);
|
||||||
|
|
||||||
public DiskSpaceService(IMovieService movieService, IDiskProvider diskProvider, Logger logger)
|
public DiskSpaceService(IMovieService movieService, IRootFolderService rootFolderService, IDiskProvider diskProvider, Logger logger)
|
||||||
{
|
{
|
||||||
_movieService = movieService;
|
_movieService = movieService;
|
||||||
|
_rootFolderService = rootFolderService;
|
||||||
_diskProvider = diskProvider;
|
_diskProvider = diskProvider;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
@ -43,9 +46,15 @@ public List<DiskSpace> GetFreeSpace()
|
|||||||
|
|
||||||
private IEnumerable<string> GetMoviesRootPaths()
|
private IEnumerable<string> GetMoviesRootPaths()
|
||||||
{
|
{
|
||||||
|
// Get all movie paths and find the correct root folder for each. For each unique root folder path,
|
||||||
|
// ensure the path exists and get its path root and return all unique path roots.
|
||||||
|
|
||||||
return _movieService.AllMoviePaths()
|
return _movieService.AllMoviePaths()
|
||||||
.Where(s => s.Value.IsPathValid(PathValidationType.CurrentOs) && _diskProvider.FolderExists(s.Value))
|
.Where(s => s.Value.IsPathValid(PathValidationType.CurrentOs))
|
||||||
.Select(s => _diskProvider.GetPathRoot(s.Value))
|
.Select(s => _rootFolderService.GetBestRootFolderPath(s.Value))
|
||||||
|
.Distinct()
|
||||||
|
.Where(r => _diskProvider.FolderExists(r))
|
||||||
|
.Select(r => _diskProvider.GetPathRoot(r))
|
||||||
.Distinct();
|
.Distinct();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using NLog;
|
using NLog;
|
||||||
using NzbDrone.Common;
|
using NzbDrone.Common;
|
||||||
|
using NzbDrone.Common.Cache;
|
||||||
using NzbDrone.Common.Disk;
|
using NzbDrone.Common.Disk;
|
||||||
using NzbDrone.Common.Extensions;
|
using NzbDrone.Common.Extensions;
|
||||||
using NzbDrone.Core.Configuration;
|
using NzbDrone.Core.Configuration;
|
||||||
@ -32,6 +33,8 @@ public class RootFolderService : IRootFolderService
|
|||||||
private readonly INamingConfigService _namingConfigService;
|
private readonly INamingConfigService _namingConfigService;
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
private readonly ICached<string> _cache;
|
||||||
|
|
||||||
private static readonly HashSet<string> SpecialFolders = new HashSet<string>
|
private static readonly HashSet<string> SpecialFolders = new HashSet<string>
|
||||||
{
|
{
|
||||||
"$recycle.bin",
|
"$recycle.bin",
|
||||||
@ -50,6 +53,7 @@ public RootFolderService(IRootFolderRepository rootFolderRepository,
|
|||||||
IMovieRepository movieRepository,
|
IMovieRepository movieRepository,
|
||||||
IConfigService configService,
|
IConfigService configService,
|
||||||
INamingConfigService namingConfigService,
|
INamingConfigService namingConfigService,
|
||||||
|
ICacheManager cacheManager,
|
||||||
Logger logger)
|
Logger logger)
|
||||||
{
|
{
|
||||||
_rootFolderRepository = rootFolderRepository;
|
_rootFolderRepository = rootFolderRepository;
|
||||||
@ -58,6 +62,8 @@ public RootFolderService(IRootFolderRepository rootFolderRepository,
|
|||||||
_configService = configService;
|
_configService = configService;
|
||||||
_namingConfigService = namingConfigService;
|
_namingConfigService = namingConfigService;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
_cache = cacheManager.GetCache<string>(GetType());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<RootFolder> All()
|
public List<RootFolder> All()
|
||||||
@ -115,7 +121,7 @@ public RootFolder Add(RootFolder rootFolder)
|
|||||||
|
|
||||||
if (!_diskProvider.FolderWritable(rootFolder.Path))
|
if (!_diskProvider.FolderWritable(rootFolder.Path))
|
||||||
{
|
{
|
||||||
throw new UnauthorizedAccessException(string.Format("Root folder path '{0}' is not writable by user '{1}'", rootFolder.Path, Environment.UserName));
|
throw new UnauthorizedAccessException($"Root folder path '{rootFolder.Path}' is not writable by user '{Environment.UserName}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
_rootFolderRepository.Insert(rootFolder);
|
_rootFolderRepository.Insert(rootFolder);
|
||||||
@ -123,6 +129,7 @@ public RootFolder Add(RootFolder rootFolder)
|
|||||||
var moviePaths = _movieRepository.AllMoviePaths();
|
var moviePaths = _movieRepository.AllMoviePaths();
|
||||||
|
|
||||||
GetDetails(rootFolder, moviePaths, true);
|
GetDetails(rootFolder, moviePaths, true);
|
||||||
|
_cache.Clear();
|
||||||
|
|
||||||
return rootFolder;
|
return rootFolder;
|
||||||
}
|
}
|
||||||
@ -130,6 +137,7 @@ public RootFolder Add(RootFolder rootFolder)
|
|||||||
public void Remove(int id)
|
public void Remove(int id)
|
||||||
{
|
{
|
||||||
_rootFolderRepository.Delete(id);
|
_rootFolderRepository.Delete(id);
|
||||||
|
_cache.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<UnmappedFolder> GetUnmappedFolders(string path, Dictionary<int, string> moviePaths)
|
private List<UnmappedFolder> GetUnmappedFolders(string path, Dictionary<int, string> moviePaths)
|
||||||
@ -201,18 +209,7 @@ public RootFolder Get(int id, bool timeout)
|
|||||||
|
|
||||||
public string GetBestRootFolderPath(string path, List<RootFolder> rootFolders = null)
|
public string GetBestRootFolderPath(string path, List<RootFolder> rootFolders = null)
|
||||||
{
|
{
|
||||||
var allRootFoldersToConsider = rootFolders ?? All();
|
return _cache.Get(path, () => GetBestRootFolderPathInternal(path, rootFolders), TimeSpan.FromDays(1));
|
||||||
|
|
||||||
var possibleRootFolder = allRootFoldersToConsider.Where(r => r.Path.IsParentPath(path)).MaxBy(r => r.Path.Length);
|
|
||||||
|
|
||||||
if (possibleRootFolder == null)
|
|
||||||
{
|
|
||||||
var osPath = new OsPath(path);
|
|
||||||
|
|
||||||
return osPath.Directory.ToString().TrimEnd(osPath.IsUnixPath ? '/' : '\\');
|
|
||||||
}
|
|
||||||
|
|
||||||
return possibleRootFolder?.Path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePaths, bool timeout)
|
private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePaths, bool timeout)
|
||||||
@ -228,5 +225,21 @@ private void GetDetails(RootFolder rootFolder, Dictionary<int, string> moviePath
|
|||||||
}
|
}
|
||||||
}).Wait(timeout ? 5000 : -1);
|
}).Wait(timeout ? 5000 : -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetBestRootFolderPathInternal(string path, List<RootFolder> rootFolders = null)
|
||||||
|
{
|
||||||
|
var allRootFoldersToConsider = rootFolders ?? All();
|
||||||
|
|
||||||
|
var possibleRootFolder = allRootFoldersToConsider.Where(r => r.Path.IsParentPath(path)).MaxBy(r => r.Path.Length);
|
||||||
|
|
||||||
|
if (possibleRootFolder == null)
|
||||||
|
{
|
||||||
|
var osPath = new OsPath(path);
|
||||||
|
|
||||||
|
return osPath.Directory.ToString().TrimEnd(osPath.IsUnixPath ? '/' : '\\');
|
||||||
|
}
|
||||||
|
|
||||||
|
return possibleRootFolder.Path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user