Browse Source

prioritize stream selection by language (#2079)

pull/2080/head
Jason Dove 2 months ago committed by GitHub
parent
commit
462057a4b1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 60
      ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs
  2. 35
      ErsatzTV.Core/FFmpeg/CustomStreamSelector.cs

60
ErsatzTV.Core.Tests/FFmpeg/CustomStreamSelectorTests.cs

@ -36,7 +36,8 @@ public class CustomStreamSelectorTests @@ -36,7 +36,8 @@ public class CustomStreamSelectorTests
new Subtitle { Id = 1, Language = "eng", Title = "Words", SubtitleKind = SubtitleKind.Embedded },
new Subtitle { Id = 2, Language = "en", Title = "Signs" },
new Subtitle { Id = 3, Language = "en", Title = "Songs" },
new Subtitle { Id = 4, Language = "en", Forced = true, SubtitleKind = SubtitleKind.Sidecar }
new Subtitle { Id = 4, Language = "en", Forced = true, SubtitleKind = SubtitleKind.Sidecar },
new Subtitle { Id = 5, Language = "jp" }
];
}
@ -73,9 +74,7 @@ items: @@ -73,9 +74,7 @@ items:
"""
---
items:
- audio_language:
- "en"
- "eng"
- audio_language: ["en", "eng"]
""";
var streamSelector = new CustomStreamSelector(
@ -584,6 +583,59 @@ items: @@ -584,6 +583,59 @@ items:
}
}
[Test]
public async Task Should_Select_Prioritized_Audio_Language()
{
const string YAML =
"""
---
items:
- audio_language: ["en*","ja"]
audio_title_blocklist: ["riff"]
""";
var streamSelector = new CustomStreamSelector(
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = YAML }]),
new NullLogger<CustomStreamSelector>());
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, _audioVersion, _subtitles);
result.AudioStream.IsSome.ShouldBeTrue();
foreach (MediaStream audioStream in result.AudioStream)
{
audioStream.Index.ShouldBe(2);
audioStream.Language.ShouldBe("eng");
}
}
[Test]
public async Task Should_Select_Prioritized_Subtitle_Language()
{
const string YAML =
"""
---
items:
- audio_language:
- "*"
subtitle_language: ["jp","en*"]
""";
var streamSelector = new CustomStreamSelector(
new FakeLocalFileSystem([new FakeFileEntry(TestFileName) { Contents = YAML }]),
new NullLogger<CustomStreamSelector>());
StreamSelectorResult result = await streamSelector.SelectStreams(_channel, _audioVersion, _subtitles);
result.Subtitle.IsSome.ShouldBeTrue();
foreach (Subtitle subtitle in result.Subtitle)
{
subtitle.Id.ShouldBe(5);
subtitle.Language.ShouldBe("jp");
}
}
private static MediaItemAudioVersion GetTestAudioVersion(string englishLanguage)
{
var mediaItem = new OtherVideo();

35
ErsatzTV.Core/FFmpeg/CustomStreamSelector.cs

@ -33,8 +33,8 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -33,8 +33,8 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
foreach (StreamSelectorItem streamSelectorItem in streamSelector.Items)
{
var candidateAudioStreams = audioStreams.ToList();
var candidateSubtitles = allSubtitles.ToList();
var candidateAudioStreams = audioStreams.ToDictionary(a => a, _ => int.MaxValue);
var candidateSubtitles = allSubtitles.ToDictionary(s => s, _ => int.MaxValue);
// try to find matching audio stream
foreach (MediaStream audioStream in audioStreams.ToList())
@ -45,8 +45,10 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -45,8 +45,10 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
if (streamSelectorItem.AudioLanguages.Count > 0)
{
// match any of the listed languages
foreach (string audioLanguage in streamSelectorItem.AudioLanguages)
for (var langIndex = 0; langIndex < streamSelectorItem.AudioLanguages.Count; langIndex++)
{
string audioLanguage = streamSelectorItem.AudioLanguages[langIndex];
// special case
if (audioLanguage == "*")
{
@ -56,6 +58,12 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -56,6 +58,12 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
matches = matches || FileSystemName.MatchesSimpleExpression(
audioLanguage.ToLowerInvariant(),
audioStream.Language.ToLowerInvariant());
// store lang index for prioritizing later
if (matches && candidateAudioStreams[audioStream] == int.MaxValue)
{
candidateAudioStreams[audioStream] = langIndex;
}
}
}
else
@ -121,8 +129,10 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -121,8 +129,10 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
if (streamSelectorItem.SubtitleLanguages.Count > 0)
{
// match any of the listed languages
foreach (string subtitleLanguage in streamSelectorItem.SubtitleLanguages)
for (var langIndex = 0; langIndex < streamSelectorItem.SubtitleLanguages.Count; langIndex++)
{
string subtitleLanguage = streamSelectorItem.SubtitleLanguages[langIndex];
// special case
if (subtitleLanguage == "*")
{
@ -132,6 +142,12 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -132,6 +142,12 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
matches = matches || FileSystemName.MatchesSimpleExpression(
subtitleLanguage,
subtitle.Language);
// store lang index for prioritizing later
if (matches && candidateSubtitles[subtitle] == int.MaxValue)
{
candidateSubtitles[subtitle] = langIndex;
}
}
}
else
@ -183,8 +199,15 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust @@ -183,8 +199,15 @@ public class CustomStreamSelector(ILocalFileSystem localFileSystem, ILogger<Cust
}
}
Option<MediaStream> maybeAudioStream = candidateAudioStreams.HeadOrNone();
Option<Subtitle> maybeSubtitle = candidateSubtitles.HeadOrNone();
Option<MediaStream> maybeAudioStream = candidateAudioStreams
.OrderBy(a => a.Value)
.Select(a => a.Key)
.HeadOrNone();
Option<Subtitle> maybeSubtitle = candidateSubtitles
.OrderBy(s => s.Value)
.Select(s => s.Key)
.HeadOrNone();
if (maybeAudioStream.IsSome)
{

Loading…
Cancel
Save