1
0
mirror of https://github.com/Sonarr/Sonarr.git synced 2026-04-23 22:25:56 -04:00

New: Import subtitles from sub folders

Closes #2513
This commit is contained in:
Stéphane Dupont
2022-02-27 21:37:23 +01:00
committed by GitHub
parent c10677dfe7
commit 4bfcd0de1d
10 changed files with 733 additions and 74 deletions
@@ -0,0 +1,206 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FizzWare.NBuilder;
using Moq;
using NUnit.Framework;
using NzbDrone.Common.Disk;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Configuration;
using NzbDrone.Core.Extras;
using NzbDrone.Core.Extras.Files;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Extras
{
[TestFixture]
public class ExtraServiceFixture : CoreTest<ExtraService>
{
private Series _series;
private EpisodeFile _episodeFile;
private LocalEpisode _localEpisode;
private string _seriesFolder;
private string _episodeFolder;
private Mock<IManageExtraFiles> _subtitleService;
private Mock<IManageExtraFiles> _otherExtraService;
[SetUp]
public void Setup()
{
_seriesFolder = @"C:\Test\TV\Series Title".AsOsAgnostic();
_episodeFolder = @"C:\Test\Unsorted TV\Series.Title.S01".AsOsAgnostic();
_series = Builder<Series>.CreateNew()
.With(s => s.Path = _seriesFolder)
.Build();
var episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeasonNumber = 1)
.Build()
.ToList();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Season 1\Series Title - S01E01.mkv".AsOsAgnostic())
.Build();
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.Series = _series)
.With(l => l.Episodes = episodes)
.With(l => l.Path = Path.Combine(_episodeFolder, "Series.Title.S01E01.mkv").AsOsAgnostic())
.Build();
_subtitleService = new Mock<IManageExtraFiles>();
_subtitleService.SetupGet(s => s.Order).Returns(0);
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalEpisode>(), It.IsAny<EpisodeFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(false);
_subtitleService.Setup(s => s.CanImportFile(It.IsAny<LocalEpisode>(), It.IsAny<EpisodeFile>(), It.IsAny<string>(), ".srt", It.IsAny<bool>()))
.Returns(true);
_otherExtraService = new Mock<IManageExtraFiles>();
_otherExtraService.SetupGet(s => s.Order).Returns(1);
_otherExtraService.Setup(s => s.CanImportFile(It.IsAny<LocalEpisode>(), It.IsAny<EpisodeFile>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(true);
Mocker.SetConstant<IEnumerable<IManageExtraFiles>>(new[] {
_subtitleService.Object,
_otherExtraService.Object
});
Mocker.GetMock<IDiskProvider>().Setup(s => s.FolderExists(It.IsAny<string>()))
.Returns(false);
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
.Returns((string path) => Directory.GetParent(path).FullName);
WithExistingFolder(_series.Path);
WithExistingFile(_episodeFile.Path);
WithExistingFile(_localEpisode.Path);
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(true);
Mocker.GetMock<IConfigService>().Setup(v => v.ExtraFileExtensions).Returns("nfo,srt");
}
private void WithExistingFolder(string path, bool exists = true)
{
var dir = Path.GetDirectoryName(path);
if (exists && dir.IsNotNullOrWhiteSpace())
{
WithExistingFolder(dir);
}
Mocker.GetMock<IDiskProvider>().Setup(v => v.FolderExists(path)).Returns(exists);
}
private void WithExistingFile(string path, bool exists = true, int size = 1000)
{
var dir = Path.GetDirectoryName(path);
if (exists && dir.IsNotNullOrWhiteSpace())
{
WithExistingFolder(dir);
}
Mocker.GetMock<IDiskProvider>().Setup(v => v.FileExists(path)).Returns(exists);
Mocker.GetMock<IDiskProvider>().Setup(v => v.GetFileSize(path)).Returns(size);
}
private void WithExistingFiles(List<string> files)
{
foreach (string file in files)
{
WithExistingFile(file);
}
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(_episodeFolder, SearchOption.AllDirectories))
.Returns(files.ToArray());
}
[Test]
public void should_not_pass_file_if_import_disabled()
{
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
var nfofile = Path.Combine(_episodeFolder, "Series.Title.S01E01.nfo").AsOsAgnostic();
var files = new List<string> {
_localEpisode.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportEpisode(_localEpisode, _episodeFile, true);
_subtitleService.Verify(v => v.CanImportFile(_localEpisode, _episodeFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
_otherExtraService.Verify(v => v.CanImportFile(_localEpisode, _episodeFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
}
[Test]
[TestCase("Series Title - S01E01.sub")]
[TestCase("Series Title - S01E01.ass")]
public void should_not_pass_unwanted_file(string filePath)
{
Mocker.GetMock<IConfigService>().Setup(v => v.ImportExtraFiles).Returns(false);
var nfofile = Path.Combine(_episodeFolder, filePath).AsOsAgnostic();
var files = new List<string> {
_localEpisode.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportEpisode(_localEpisode, _episodeFile, true);
_subtitleService.Verify(v => v.CanImportFile(_localEpisode, _episodeFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
_otherExtraService.Verify(v => v.CanImportFile(_localEpisode, _episodeFile, It.IsAny<string>(), It.IsAny<string>(), true), Times.Never());
}
[Test]
public void should_pass_subtitle_file_to_subtitle_service()
{
var subtitleFile = Path.Combine(_episodeFolder, "Series.Title.S01E01.en.srt").AsOsAgnostic();
var files = new List<string> {
_localEpisode.Path,
subtitleFile
};
WithExistingFiles(files);
Subject.ImportEpisode(_localEpisode, _episodeFile, true);
_subtitleService.Verify(v => v.ImportFiles(_localEpisode, _episodeFile, new List<string> { subtitleFile }, true), Times.Once());
_otherExtraService.Verify(v => v.ImportFiles(_localEpisode, _episodeFile, new List<string> { subtitleFile }, true), Times.Never());
}
[Test]
public void should_pass_nfo_file_to_other_service()
{
var nfofile = Path.Combine(_episodeFolder, "Series.Title.S01E01.nfo").AsOsAgnostic();
var files = new List<string> {
_localEpisode.Path,
nfofile
};
WithExistingFiles(files);
Subject.ImportEpisode(_localEpisode, _episodeFile, true);
_subtitleService.Verify(v => v.ImportFiles(_localEpisode, _episodeFile, new List<string> { nfofile }, true), Times.Never());
_otherExtraService.Verify(v => v.ImportFiles(_localEpisode, _episodeFile, new List<string> { nfofile }, true), Times.Once());
}
}
}
@@ -0,0 +1,94 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using FizzWare.NBuilder;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Others;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
namespace NzbDrone.Core.Test.Extras.Others
{
[TestFixture]
public class OtherExtraServiceFixture : CoreTest<OtherExtraService>
{
private Series _series;
private EpisodeFile _episodeFile;
private LocalEpisode _localEpisode;
private string _seriesFolder;
private string _episodeFolder;
[SetUp]
public void Setup()
{
_seriesFolder = @"C:\Test\TV\Series Title".AsOsAgnostic();
_episodeFolder = @"C:\Test\Unsorted TV\Series.Title.S01".AsOsAgnostic();
_series = Builder<Series>.CreateNew()
.With(s => s.Path = _seriesFolder)
.Build();
var episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeasonNumber = 1)
.Build()
.ToList();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Season 1\Series Title - S01E01.mkv")
.Build();
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.Series = _series)
.With(l => l.Episodes = episodes)
.With(l => l.Path = Path.Combine(_episodeFolder, "Series.Title.S01E01.mkv").AsOsAgnostic())
.With(l => l.FileEpisodeInfo = new ParsedEpisodeInfo
{
SeasonNumber = 1,
EpisodeNumbers = new[] { 1 }
})
.Build();
}
[Test]
[TestCase("Series Title - S01E01.nfo", "Series Title - S01E01.nfo")]
[TestCase("Series.Title.S01E01.nfo", "Series Title - S01E01.nfo")]
[TestCase("Series-Title-S01E01.nfo", "Series Title - S01E01.nfo")]
[TestCase("Series Title S01E01.nfo", "Series Title - S01E01.nfo")]
[TestCase("Series_Title_S01E01.nfo", "Series Title - S01E01.nfo")]
[TestCase("S01E01.thumb.jpg", "Series Title - S01E01.jpg")]
[TestCase(@"Series.Title.S01E01\thumb.jpg", "Series Title - S01E01.jpg")]
public void should_import_matching_file(string filePath, string expectedOutputPath)
{
var files = new List<string> { Path.Combine(_episodeFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputPath).AsOsAgnostic()).Should().Be(true);
}
[Test]
public void should_not_import_multiple_nfo_files()
{
var files = new List<string>
{
Path.Combine(_episodeFolder, "Series.Title.S01E01.nfo").AsOsAgnostic(),
Path.Combine(_episodeFolder, "Series_Title_S01E01.nfo").AsOsAgnostic(),
};
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
results.Count().Should().Be(1);
}
}
}
@@ -0,0 +1,181 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using FizzWare.NBuilder;
using NUnit.Framework;
using NzbDrone.Common.Extensions;
using NzbDrone.Core.Extras.Subtitles;
using NzbDrone.Core.MediaFiles;
using NzbDrone.Core.MediaFiles.EpisodeImport;
using NzbDrone.Core.Parser.Model;
using NzbDrone.Core.Test.Framework;
using NzbDrone.Core.Tv;
using NzbDrone.Test.Common;
using Moq;
using NzbDrone.Common.Disk;
namespace NzbDrone.Core.Test.Extras.Subtitles
{
[TestFixture]
public class SubtitleServiceFixture : CoreTest<SubtitleService>
{
private Series _series;
private EpisodeFile _episodeFile;
private LocalEpisode _localEpisode;
private string _seriesFolder;
private string _episodeFolder;
[SetUp]
public void Setup()
{
_seriesFolder = @"C:\Test\TV\Series Title".AsOsAgnostic();
_episodeFolder = @"C:\Test\Unsorted TV\Series.Title.S01".AsOsAgnostic();
_series = Builder<Series>.CreateNew()
.With(s => s.Path = _seriesFolder)
.Build();
var episodes = Builder<Episode>.CreateListOfSize(1)
.All()
.With(e => e.SeasonNumber = 1)
.Build()
.ToList();
_episodeFile = Builder<EpisodeFile>.CreateNew()
.With(f => f.Path = Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.mkv").AsOsAgnostic())
.With(f => f.RelativePath = @"Season 1\Series Title - S01E01.mkv".AsOsAgnostic())
.Build();
_localEpisode = Builder<LocalEpisode>.CreateNew()
.With(l => l.Series = _series)
.With(l => l.Episodes = episodes)
.With(l => l.Path = Path.Combine(_episodeFolder, "Series.Title.S01E01.mkv").AsOsAgnostic())
.With(l => l.FileEpisodeInfo = new ParsedEpisodeInfo
{
SeasonNumber = 1,
EpisodeNumbers = new[] { 1 }
})
.Build();
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetParentFolder(It.IsAny<string>()))
.Returns((string path) => Directory.GetParent(path).FullName);
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<Series>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns(DetectSampleResult.NotSample);
}
[Test]
[TestCase("Series.Title.S01E01.en.nfo")]
public void should_not_import_non_subtitle_file(string filePath)
{
var files = new List<string> { Path.Combine(_episodeFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
results.Count().Should().Be(0);
}
[Test]
[TestCase("Series Title - S01E01.srt", "Series Title - S01E01.srt")]
[TestCase("Series.Title.S01E01.en.srt", "Series Title - S01E01.en.srt")]
[TestCase("Series.Title.S01E01.english.srt", "Series Title - S01E01.en.srt")]
[TestCase("Series-Title-S01E01-fr-cc.srt", "Series Title - S01E01.fr.srt")]
[TestCase("Series Title S01E01_en_sdh_forced.srt", "Series Title - S01E01.en.srt")]
[TestCase("Series_Title_S01E01 en.srt", "Series Title - S01E01.en.srt")]
[TestCase(@"Subs\S01E01.en.srt", "Series Title - S01E01.en.srt")]
[TestCase(@"Subs\Series.Title.S01E01\2_en.srt", "Series Title - S01E01.en.srt")]
public void should_import_matching_subtitle_file(string filePath, string expectedOutputPath)
{
var files = new List<string> { Path.Combine(_episodeFolder, filePath).AsOsAgnostic() };
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputPath).AsOsAgnostic()).Should().Be(true);
}
[Test]
public void should_import_multiple_subtitle_files_per_language()
{
var files = new List<string>
{
Path.Combine(_episodeFolder, "Series.Title.S01E01.en.srt").AsOsAgnostic(),
Path.Combine(_episodeFolder, "Series.Title.S01E01.english.srt").AsOsAgnostic(),
Path.Combine(_episodeFolder, "Subs", "Series_Title_S01E01_en_forced.srt").AsOsAgnostic(),
Path.Combine(_episodeFolder, "Subs", "Series.Title.S01E01", "2_fr.srt").AsOsAgnostic()
};
var expectedOutputs = new string[]
{
"Series Title - S01E01.1.en.srt",
"Series Title - S01E01.2.en.srt",
"Series Title - S01E01.3.en.srt",
"Series Title - S01E01.fr.srt",
};
var results = Subject.ImportFiles(_localEpisode, _episodeFile, files, true).ToList();
results.Count().Should().Be(expectedOutputs.Length);
for (int i = 0; i < expectedOutputs.Length; i++)
{
results[i].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputs[i]).AsOsAgnostic()).Should().Be(true);
}
}
[Test]
[TestCase("sub.srt", "Series Title - S01E01.srt")]
[TestCase(@"Subs\2_en.srt", "Series Title - S01E01.en.srt")]
public void should_import_unmatching_subtitle_file_if_only_episode(string filePath, string expectedOutputPath)
{
var subtitleFile = Path.Combine(_episodeFolder, filePath).AsOsAgnostic();
var sampleFile = Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.sample.mkv").AsOsAgnostic();
var videoFiles = new string[]
{
_localEpisode.Path,
sampleFile
};
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
.Returns(videoFiles);
Mocker.GetMock<IDetectSample>().Setup(s => s.IsSample(It.IsAny<Series>(), sampleFile, It.IsAny<bool>()))
.Returns(DetectSampleResult.Sample);
var results = Subject.ImportFiles(_localEpisode, _episodeFile, new List<string> { subtitleFile }, true).ToList();
results.Count().Should().Be(1);
results[0].RelativePath.AsOsAgnostic().PathEquals(Path.Combine("Season 1", expectedOutputPath).AsOsAgnostic()).Should().Be(true);
ExceptionVerification.ExpectedWarns(1);
}
[Test]
[TestCase("sub.srt")]
[TestCase(@"Subs\2_en.srt")]
public void should_not_import_unmatching_subtitle_file_if_multiple_episodes(string filePath)
{
var subtitleFile = Path.Combine(_episodeFolder, filePath).AsOsAgnostic();
var videoFiles = new string[]
{
_localEpisode.Path,
Path.Combine(_series.Path, "Season 1", "Series Title - S01E01.sample.mkv").AsOsAgnostic()
};
Mocker.GetMock<IDiskProvider>().Setup(s => s.GetFiles(It.IsAny<string>(), SearchOption.AllDirectories))
.Returns(videoFiles);
var results = Subject.ImportFiles(_localEpisode, _episodeFile, new List<string> { subtitleFile }, true).ToList();
results.Count().Should().Be(0);
}
}
}