New: Use Series Folder Format to improve unmapped folders within root folders
This commit is contained in:
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…
Reference in New Issue