New: Use Series Folder Format to improve unmapped folders within root folders

pull/5428/head
Mark McDowall 2 years ago
parent 119addd75f
commit 81d2b18ce1

@ -10,6 +10,7 @@ import styles from './ImportSeriesRow.css';
function ImportSeriesRow(props) { function ImportSeriesRow(props) {
const { const {
id, id,
relativePath,
monitor, monitor,
qualityProfileId, qualityProfileId,
seasonFolder, seasonFolder,
@ -32,7 +33,7 @@ function ImportSeriesRow(props) {
/> />
<VirtualTableRowCell className={styles.folder}> <VirtualTableRowCell className={styles.folder}>
{id} {relativePath}
</VirtualTableRowCell> </VirtualTableRowCell>
<VirtualTableRowCell className={styles.monitor}> <VirtualTableRowCell className={styles.monitor}>
@ -84,6 +85,7 @@ function ImportSeriesRow(props) {
ImportSeriesRow.propTypes = { ImportSeriesRow.propTypes = {
id: PropTypes.string.isRequired, id: PropTypes.string.isRequired,
relativePath: PropTypes.string.isRequired,
monitor: PropTypes.string.isRequired, monitor: PropTypes.string.isRequired,
qualityProfileId: PropTypes.number.isRequired, qualityProfileId: PropTypes.number.isRequired,
seriesType: PropTypes.string.isRequired, seriesType: PropTypes.string.isRequired,

@ -32,7 +32,7 @@ class ImportSeriesTable extends Component {
unmappedFolders.forEach((unmappedFolder) => { unmappedFolders.forEach((unmappedFolder) => {
const id = unmappedFolder.name; const id = unmappedFolder.name;
onSeriesLookup(id, unmappedFolder.path); onSeriesLookup(id, unmappedFolder.path, unmappedFolder.relativePath);
onSetImportSeriesValue({ onSetImportSeriesValue({
id, id,

@ -26,10 +26,11 @@ function createMapStateToProps() {
function createMapDispatchToProps(dispatch, props) { function createMapDispatchToProps(dispatch, props) {
return { return {
onSeriesLookup(name, path) { onSeriesLookup(name, path, relativePath) {
dispatch(queueLookupSeries({ dispatch(queueLookupSeries({
name, name,
path, path,
relativePath,
term: name term: name
})); }));
}, },

@ -67,6 +67,7 @@ export const actionHandlers = handleThunks({
const { const {
name, name,
path, path,
relativePath,
term, term,
topOfQueue = false topOfQueue = false
} = payload; } = payload;
@ -76,6 +77,7 @@ export const actionHandlers = handleThunks({
id: name, id: name,
term, term,
path, path,
relativePath,
isFetching: false, isFetching: false,
isPopulated: false, isPopulated: false,
error: null error: null

@ -7,6 +7,7 @@ using FluentAssertions;
using Moq; using Moq;
using NUnit.Framework; using NUnit.Framework;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.RootFolders; using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Test.Framework; using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
@ -17,9 +18,13 @@ namespace NzbDrone.Core.Test.RootFolderTests
[TestFixture] [TestFixture]
public class RootFolderServiceFixture : CoreTest<RootFolderService> public class RootFolderServiceFixture : CoreTest<RootFolderService>
{ {
private NamingConfig _namingConfig;
[SetUp] [SetUp]
public void Setup() public void Setup()
{ {
_namingConfig = NamingConfig.Default;
Mocker.GetMock<IDiskProvider>() Mocker.GetMock<IDiskProvider>()
.Setup(m => m.FolderExists(It.IsAny<string>())) .Setup(m => m.FolderExists(It.IsAny<string>()))
.Returns(true); .Returns(true);
@ -31,6 +36,10 @@ namespace NzbDrone.Core.Test.RootFolderTests
Mocker.GetMock<IRootFolderRepository>() Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.All()) .Setup(s => s.All())
.Returns(new List<RootFolder>()); .Returns(new List<RootFolder>());
Mocker.GetMock<INamingConfigService>()
.Setup(c => c.GetConfig())
.Returns(_namingConfig);
} }
private void WithNonExistingFolder() private void WithNonExistingFolder()
@ -140,5 +149,47 @@ namespace NzbDrone.Core.Test.RootFolderTests
unmappedFolders.Count.Should().BeGreaterThan(0); unmappedFolders.Count.Should().BeGreaterThan(0);
unmappedFolders.Should().NotContain(u => u.Name == subFolder); unmappedFolders.Should().NotContain(u => u.Name == subFolder);
} }
[Test]
public void should_get_unmapped_folders_inside_letter_subfolder()
{
_namingConfig.SeriesFolderFormat = "{Series TitleFirstCharacter}\\{Series Title}";
var rootFolderPath = @"C:\Test\TV".AsOsAgnostic();
var rootFolder = Builder<RootFolder>.CreateNew()
.With(r => r.Path = rootFolderPath)
.Build();
var subFolderPath = Path.Combine(rootFolderPath, "S");
var subFolders = new[]
{
"Series1",
"Series2",
"Series3",
};
var folders = subFolders.Select(f => Path.Combine(subFolderPath, f)).ToArray();
Mocker.GetMock<IRootFolderRepository>()
.Setup(s => s.Get(It.IsAny<int>()))
.Returns(rootFolder);
Mocker.GetMock<ISeriesRepository>()
.Setup(s => s.AllSeriesPaths())
.Returns(new Dictionary<int, string>());
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(rootFolder.Path))
.Returns(new[] { subFolderPath });
Mocker.GetMock<IDiskProvider>()
.Setup(s => s.GetDirectories(subFolderPath))
.Returns(folders);
var unmappedFolders = Subject.Get(rootFolder.Id, false).UnmappedFolders;
unmappedFolders.Count.Should().Be(3);
}
} }
} }

@ -6,7 +6,6 @@ namespace NzbDrone.Core.RootFolders
public class RootFolder : ModelBase public class RootFolder : ModelBase
{ {
public string Path { get; set; } public string Path { get; set; }
public bool Accessible { get; set; } public bool Accessible { get; set; }
public long? FreeSpace { get; set; } public long? FreeSpace { get; set; }
public long? TotalSpace { get; set; } public long? TotalSpace { get; set; }

@ -7,6 +7,7 @@ using NLog;
using NzbDrone.Common; using NzbDrone.Common;
using NzbDrone.Common.Disk; using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Organizer;
using NzbDrone.Core.Tv; using NzbDrone.Core.Tv;
namespace NzbDrone.Core.RootFolders namespace NzbDrone.Core.RootFolders
@ -26,6 +27,7 @@ namespace NzbDrone.Core.RootFolders
private readonly IRootFolderRepository _rootFolderRepository; private readonly IRootFolderRepository _rootFolderRepository;
private readonly IDiskProvider _diskProvider; private readonly IDiskProvider _diskProvider;
private readonly ISeriesRepository _seriesRepository; private readonly ISeriesRepository _seriesRepository;
private readonly INamingConfigService _namingConfigService;
private readonly Logger _logger; private readonly Logger _logger;
private static readonly HashSet<string> SpecialFolders = new HashSet<string> private static readonly HashSet<string> SpecialFolders = new HashSet<string>
@ -44,11 +46,13 @@ namespace NzbDrone.Core.RootFolders
public RootFolderService(IRootFolderRepository rootFolderRepository, public RootFolderService(IRootFolderRepository rootFolderRepository,
IDiskProvider diskProvider, IDiskProvider diskProvider,
ISeriesRepository seriesRepository, ISeriesRepository seriesRepository,
INamingConfigService namingConfigService,
Logger logger) Logger logger)
{ {
_rootFolderRepository = rootFolderRepository; _rootFolderRepository = rootFolderRepository;
_diskProvider = diskProvider; _diskProvider = diskProvider;
_seriesRepository = seriesRepository; _seriesRepository = seriesRepository;
_namingConfigService = namingConfigService;
_logger = logger; _logger = logger;
} }
@ -139,13 +143,28 @@ namespace NzbDrone.Core.RootFolders
return results; return results;
} }
var subFolderDepth = _namingConfigService.GetConfig().SeriesFolderFormat.Count(f => f == Path.DirectorySeparatorChar);
var possibleSeriesFolders = _diskProvider.GetDirectories(path).ToList(); var possibleSeriesFolders = _diskProvider.GetDirectories(path).ToList();
if (subFolderDepth > 0)
{
for (int i = 0; i < subFolderDepth; i++)
{
possibleSeriesFolders = possibleSeriesFolders.SelectMany(_diskProvider.GetDirectories).ToList();
}
}
var unmappedFolders = possibleSeriesFolders.Except(seriesPaths.Select(s => s.Value), PathEqualityComparer.Instance).ToList(); var unmappedFolders = possibleSeriesFolders.Except(seriesPaths.Select(s => s.Value), PathEqualityComparer.Instance).ToList();
foreach (string unmappedFolder in unmappedFolders) foreach (var unmappedFolder in unmappedFolders)
{ {
var di = new DirectoryInfo(unmappedFolder.Normalize()); var di = new DirectoryInfo(unmappedFolder.Normalize());
results.Add(new UnmappedFolder { Name = di.Name, Path = di.FullName }); results.Add(new UnmappedFolder
{
Name = di.Name,
Path = di.FullName,
RelativePath = path.GetRelativePath(di.FullName)
});
} }
var setToRemove = SpecialFolders; var setToRemove = SpecialFolders;
@ -181,8 +200,8 @@ namespace NzbDrone.Core.RootFolders
private void GetDetails(RootFolder rootFolder, Dictionary<int, string> seriesPaths, bool timeout) private void GetDetails(RootFolder rootFolder, Dictionary<int, string> seriesPaths, bool timeout)
{ {
Task.Run(() => // Task.Run(() =>
{ // {
if (_diskProvider.FolderExists(rootFolder.Path)) if (_diskProvider.FolderExists(rootFolder.Path))
{ {
rootFolder.Accessible = true; rootFolder.Accessible = true;
@ -190,7 +209,8 @@ namespace NzbDrone.Core.RootFolders
rootFolder.TotalSpace = _diskProvider.GetTotalSize(rootFolder.Path); rootFolder.TotalSpace = _diskProvider.GetTotalSize(rootFolder.Path);
rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path, seriesPaths); rootFolder.UnmappedFolders = GetUnmappedFolders(rootFolder.Path, seriesPaths);
} }
}).Wait(timeout ? 5000 : -1);
// }).Wait(timeout ? 5000 : -1);
} }
} }
} }

@ -1,8 +1,9 @@
namespace NzbDrone.Core.RootFolders namespace NzbDrone.Core.RootFolders
{ {
public class UnmappedFolder public class UnmappedFolder
{ {
public string Name { get; set; } public string Name { get; set; }
public string Path { get; set; } public string Path { get; set; }
public string RelativePath { get; set; }
} }
} }

Loading…
Cancel
Save