support external subtitles (#745)
* support external subtitles in local movie libraries * code cleanup * simplify subtitle updating * skip external subtitles from media servers * fix plex subtitles
This commit is contained in:
@@ -531,8 +531,10 @@ public class TranscodingTests
|
||||
public Task<Option<MediaStream>> SelectAudioStream(Channel channel, MediaVersion version) =>
|
||||
Optional(version.Streams.First(s => s.MediaStreamKind == MediaStreamKind.Audio)).AsTask();
|
||||
|
||||
public Task<Option<MediaStream>> SelectSubtitleStream(Channel channel, MediaVersion version) =>
|
||||
Optional(version.Streams.Find(s => s.MediaStreamKind == MediaStreamKind.Subtitle)).AsTask();
|
||||
public Task<Option<Domain.Subtitle>> SelectSubtitleStream(
|
||||
Channel channel,
|
||||
MediaVersion version,
|
||||
List<Domain.Subtitle> subtitles) => subtitles.HeadOrNone().AsTask();
|
||||
}
|
||||
|
||||
private static string ExecutableName(string baseName) =>
|
||||
|
||||
@@ -45,6 +45,10 @@ public class FakeLocalFileSystem : ILocalFileSystem
|
||||
public IEnumerable<string> ListFiles(string folder) =>
|
||||
_files.Map(f => f.Path).Filter(f => Path.GetDirectoryName(f) == folder);
|
||||
|
||||
// TODO: this isn't accurate, need to use search pattern
|
||||
public IEnumerable<string> ListFiles(string folder, string searchPattern) =>
|
||||
_files.Map(f => f.Path).Filter(f => Path.GetDirectoryName(f) == folder);
|
||||
|
||||
public bool FileExists(string path) => _files.Any(f => f.Path == path);
|
||||
public bool FolderExists(string folder) => false;
|
||||
|
||||
|
||||
@@ -67,7 +67,9 @@ public class FakeTelevisionRepository : ITelevisionRepository
|
||||
public Task<Either<BaseError, PlexSeason>> GetOrAddPlexSeason(PlexLibrary library, PlexSeason item) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<Either<BaseError, PlexEpisode>> GetOrAddPlexEpisode(PlexLibrary library, PlexEpisode item) =>
|
||||
public Task<Either<BaseError, MediaItemScanResult<PlexEpisode>>> GetOrAddPlexEpisode(
|
||||
PlexLibrary library,
|
||||
PlexEpisode item) =>
|
||||
throw new NotSupportedException();
|
||||
|
||||
public Task<bool> AddGenre(ShowMetadata metadata, Genre genre) => throw new NotSupportedException();
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
using System.Globalization;
|
||||
using ErsatzTV.Core.Domain;
|
||||
using ErsatzTV.Core.Interfaces.Repositories;
|
||||
using ErsatzTV.Core.Metadata;
|
||||
using ErsatzTV.Core.Tests.Fakes;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Moq;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace ErsatzTV.Core.Tests.Metadata;
|
||||
|
||||
[TestFixture]
|
||||
public class LocalSubtitlesProviderTests
|
||||
{
|
||||
// test cases are from plex's example folder layout here
|
||||
// https://support.plex.tv/articles/200471133-adding-local-subtitles-to-your-media/
|
||||
// /Movies
|
||||
// /Avatar (2009)
|
||||
// Avatar (2009).mkv
|
||||
// Avatar (2009).eng.srt
|
||||
// Avatar (2009).en.forced.ass
|
||||
// Avatar (2009).en.sdh.srt
|
||||
// Avatar (2009).de.srt
|
||||
// Avatar (2009).de.sdh.forced.srt
|
||||
|
||||
[Test]
|
||||
public void Should_Find_All_Languages_Codecs_And_Flags_With_Full_Paths()
|
||||
{
|
||||
// normally this will have a full list from the database, but we just need these two for testing
|
||||
var cultures = new List<CultureInfo>
|
||||
{
|
||||
CultureInfo.GetCultureInfo("en-US"),
|
||||
CultureInfo.GetCultureInfo("de-DE")
|
||||
};
|
||||
|
||||
var fakeFiles = new List<FakeFileEntry>
|
||||
{
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).mkv"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).eng.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).en.forced.ass"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).en.sdh.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).de.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).de.sdh.forced.srt")
|
||||
};
|
||||
|
||||
var provider = new LocalSubtitlesProvider(
|
||||
new Mock<IMediaItemRepository>().Object,
|
||||
new Mock<IMetadataRepository>().Object,
|
||||
new FakeLocalFileSystem(fakeFiles),
|
||||
new Mock<ILogger<LocalSubtitlesProvider>>().Object);
|
||||
|
||||
List<Subtitle> result = provider.LocateExternalSubtitles(
|
||||
cultures,
|
||||
@"/Movies/Avatar (2009)/Avatar (2009).mkv",
|
||||
true);
|
||||
|
||||
result.Count.Should().Be(5);
|
||||
result.Count(s => s.Language == "eng").Should().Be(3);
|
||||
result.Count(s => s.Language == "deu").Should().Be(2);
|
||||
result.Count(s => s.Forced).Should().Be(2);
|
||||
result.Count(s => s.SDH).Should().Be(2);
|
||||
result.Count(s => s.Codec == "subrip").Should().Be(4);
|
||||
result.Count(s => s.Codec == "ass").Should().Be(1);
|
||||
result.All(s => s.Path.Contains(@"/Movies/Avatar (2009)/")).Should().BeTrue();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Should_Find_All_Languages_Codecs_And_Flags_With_File_Names()
|
||||
{
|
||||
// normally this will have a full list from the database, but we just need these two for testing
|
||||
var cultures = new List<CultureInfo>
|
||||
{
|
||||
CultureInfo.GetCultureInfo("en-US"),
|
||||
CultureInfo.GetCultureInfo("de-DE")
|
||||
};
|
||||
|
||||
var fakeFiles = new List<FakeFileEntry>
|
||||
{
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).mkv"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).eng.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).en.forced.ass"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).en.sdh.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).de.srt"),
|
||||
new(@"/Movies/Avatar (2009)/Avatar (2009).de.sdh.forced.srt")
|
||||
};
|
||||
|
||||
var provider = new LocalSubtitlesProvider(
|
||||
new Mock<IMediaItemRepository>().Object,
|
||||
new Mock<IMetadataRepository>().Object,
|
||||
new FakeLocalFileSystem(fakeFiles),
|
||||
new Mock<ILogger<LocalSubtitlesProvider>>().Object);
|
||||
|
||||
List<Subtitle> result = provider.LocateExternalSubtitles(
|
||||
cultures,
|
||||
@"/Movies/Avatar (2009)/Avatar (2009).mkv",
|
||||
false);
|
||||
|
||||
result.Count.Should().Be(5);
|
||||
result.Count(s => s.Language == "eng").Should().Be(3);
|
||||
result.Count(s => s.Language == "deu").Should().Be(2);
|
||||
result.Count(s => s.Forced).Should().Be(2);
|
||||
result.Count(s => s.SDH).Should().Be(2);
|
||||
result.Count(s => s.Codec == "subrip").Should().Be(4);
|
||||
result.Count(s => s.Codec == "ass").Should().Be(1);
|
||||
result.Count(s => s.Path.Contains(@"/Movies/Avatar (2009)/")).Should().Be(0);
|
||||
}
|
||||
}
|
||||
@@ -617,6 +617,7 @@ public class MovieFolderScannerTests
|
||||
new FakeLocalFileSystem(new List<FakeFileEntry>(files)),
|
||||
_movieRepository.Object,
|
||||
_localStatisticsProvider.Object,
|
||||
new Mock<ILocalSubtitlesProvider>().Object,
|
||||
_localMetadataProvider.Object,
|
||||
new Mock<IMetadataRepository>().Object,
|
||||
_imageCache.Object,
|
||||
@@ -636,6 +637,7 @@ public class MovieFolderScannerTests
|
||||
new FakeLocalFileSystem(new List<FakeFileEntry>(), new List<FakeFolderEntry>(folders)),
|
||||
_movieRepository.Object,
|
||||
_localStatisticsProvider.Object,
|
||||
new Mock<ILocalSubtitlesProvider>().Object,
|
||||
_localMetadataProvider.Object,
|
||||
new Mock<IMetadataRepository>().Object,
|
||||
_imageCache.Object,
|
||||
|
||||
Reference in New Issue
Block a user