Browse Source

add chapter title to filler expression (#2803)

* fix transcoding tests

* pass chapter title to filler expression

* update changelog
pull/2804/head
Jason Dove 6 months ago committed by GitHub
parent
commit
f1072b70c7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 3
      CHANGELOG.md
  2. 102
      ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs
  3. 10
      ErsatzTV.Core/Scheduling/FillerExpression.cs
  4. 3
      ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

3
CHANGELOG.md

@ -29,6 +29,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -29,6 +29,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- `Normalize Video` (default: true) - normalizes video streams, or stream copies when disabled
- `Normalize Colors` (default: true) - normalizes color parameters when enabled
- Disabling any of these options may have a significant performance benefit *at the expense of stream stability*
- Add chapter `title` to filler expression
- This can be used to include or exclude chapters with specific (case-insensitive) titles
- E.g. `title == 'here'`, `title != 'not here'`, `title like '%here%'`
### Changed
- Disable automatic artwork database cleanup

102
ErsatzTV.Core.Tests/Scheduling/FillerExpressionTests.cs

@ -75,4 +75,106 @@ public class FillerExpressionTests @@ -75,4 +75,106 @@ public class FillerExpressionTests
result[1].EndTime.ShouldBe(TimeSpan.FromMinutes(20));
result[2].EndTime.ShouldBe(TimeSpan.FromMinutes(30));
}
[Test]
public void Match_Case_Insensitive_Titles_Expression()
{
// 30 min content
var playoutItem = new PlayoutItem { Start = DateTimeOffset.Now.UtcDateTime };
playoutItem.Finish = playoutItem.Start + TimeSpan.FromMinutes(30);
// chapters every 5 min
var chapters = new List<MediaChapter>
{
new() { ChapterId = 1, StartTime = TimeSpan.Zero, EndTime = TimeSpan.FromMinutes(5), Title = "Not Here" },
new() { ChapterId = 2, StartTime = TimeSpan.FromMinutes(5), EndTime = TimeSpan.FromMinutes(10), Title = "Here" },
new() { ChapterId = 3, StartTime = TimeSpan.FromMinutes(10), EndTime = TimeSpan.FromMinutes(15), Title = "Not Here" },
new() { ChapterId = 4, StartTime = TimeSpan.FromMinutes(15), EndTime = TimeSpan.FromMinutes(20), Title = "Here" },
new() { ChapterId = 5, StartTime = TimeSpan.FromMinutes(20), EndTime = TimeSpan.FromMinutes(25), Title = "Not Here" },
new() { ChapterId = 6, StartTime = TimeSpan.FromMinutes(25), EndTime = TimeSpan.FromMinutes(30), Title = "Here" }
};
var fillerPreset = new FillerPreset
{
FillerKind = FillerKind.MidRoll,
Expression =
"title == 'here'"
};
List<MediaChapter> result = FillerExpression.FilterChapters(fillerPreset.Expression, chapters, playoutItem);
result.Count.ShouldBe(3);
result[0].EndTime.ShouldBe(TimeSpan.FromMinutes(10));
result[1].EndTime.ShouldBe(TimeSpan.FromMinutes(20));
result[2].EndTime.ShouldBe(TimeSpan.FromMinutes(30));
}
[Test]
public void Exclude_Case_Insensitive_Titles_Expression()
{
// 30 min content
var playoutItem = new PlayoutItem { Start = DateTimeOffset.Now.UtcDateTime };
playoutItem.Finish = playoutItem.Start + TimeSpan.FromMinutes(30);
// chapters every 5 min
var chapters = new List<MediaChapter>
{
new() { ChapterId = 1, StartTime = TimeSpan.Zero, EndTime = TimeSpan.FromMinutes(5), Title = "Not Here" },
new() { ChapterId = 2, StartTime = TimeSpan.FromMinutes(5), EndTime = TimeSpan.FromMinutes(10), Title = "Here" },
new() { ChapterId = 3, StartTime = TimeSpan.FromMinutes(10), EndTime = TimeSpan.FromMinutes(15), Title = "Not Here" },
new() { ChapterId = 4, StartTime = TimeSpan.FromMinutes(15), EndTime = TimeSpan.FromMinutes(20), Title = "Here" },
new() { ChapterId = 5, StartTime = TimeSpan.FromMinutes(20), EndTime = TimeSpan.FromMinutes(25), Title = "Not Here" },
new() { ChapterId = 6, StartTime = TimeSpan.FromMinutes(25), EndTime = TimeSpan.FromMinutes(30), Title = "Here" }
};
var fillerPreset = new FillerPreset
{
FillerKind = FillerKind.MidRoll,
Expression =
"title != 'not here'"
};
List<MediaChapter> result = FillerExpression.FilterChapters(fillerPreset.Expression, chapters, playoutItem);
result.Count.ShouldBe(3);
result[0].EndTime.ShouldBe(TimeSpan.FromMinutes(10));
result[1].EndTime.ShouldBe(TimeSpan.FromMinutes(20));
result[2].EndTime.ShouldBe(TimeSpan.FromMinutes(30));
}
[Test]
public void Include_Partial_Case_Insensitive_Titles_Expression()
{
// 30 min content
var playoutItem = new PlayoutItem { Start = DateTimeOffset.Now.UtcDateTime };
playoutItem.Finish = playoutItem.Start + TimeSpan.FromMinutes(30);
// chapters every 5 min
var chapters = new List<MediaChapter>
{
new() { ChapterId = 1, StartTime = TimeSpan.Zero, EndTime = TimeSpan.FromMinutes(5), Title = "Not Here" },
new() { ChapterId = 2, StartTime = TimeSpan.FromMinutes(5), EndTime = TimeSpan.FromMinutes(10), Title = "Here" },
new() { ChapterId = 3, StartTime = TimeSpan.FromMinutes(10), EndTime = TimeSpan.FromMinutes(15), Title = "Not Here" },
new() { ChapterId = 4, StartTime = TimeSpan.FromMinutes(15), EndTime = TimeSpan.FromMinutes(20), Title = "Here" },
new() { ChapterId = 5, StartTime = TimeSpan.FromMinutes(20), EndTime = TimeSpan.FromMinutes(25), Title = "Not Here" },
new() { ChapterId = 6, StartTime = TimeSpan.FromMinutes(25), EndTime = TimeSpan.FromMinutes(30), Title = "Here" }
};
var fillerPreset = new FillerPreset
{
FillerKind = FillerKind.MidRoll,
Expression =
"title like \"%here%\""
};
List<MediaChapter> result = FillerExpression.FilterChapters(fillerPreset.Expression, chapters, playoutItem);
result.Count.ShouldBe(6);
result[0].EndTime.ShouldBe(TimeSpan.FromMinutes(5));
result[1].EndTime.ShouldBe(TimeSpan.FromMinutes(10));
result[2].EndTime.ShouldBe(TimeSpan.FromMinutes(15));
result[3].EndTime.ShouldBe(TimeSpan.FromMinutes(20));
result[4].EndTime.ShouldBe(TimeSpan.FromMinutes(25));
result[5].EndTime.ShouldBe(TimeSpan.FromMinutes(30));
}
}

10
ErsatzTV.Core/Scheduling/FillerExpression.cs

@ -15,7 +15,7 @@ public static class FillerExpression @@ -15,7 +15,7 @@ public static class FillerExpression
return effectiveChapters;
}
var chapterPoints = effectiveChapters.Map(c => c.EndTime).SkipLast().ToList();
var candidateChapters = effectiveChapters.SkipLast().ToList();
var newChapters = new List<MediaChapter>();
@ -26,10 +26,11 @@ public static class FillerExpression @@ -26,10 +26,11 @@ public static class FillerExpression
double contentDuration = (playoutItem.FinishOffset - playoutItem.StartOffset).TotalSeconds;
var matches = 0;
for (var index = 0; index < chapterPoints.Count; index++)
for (var index = 0; index < candidateChapters.Count; index++)
{
TimeSpan chapterPoint = chapterPoints[index];
var expression = new Expression(fillerExpression);
MediaChapter chapter = candidateChapters[index];
TimeSpan chapterPoint = chapter.EndTime;
var expression = new Expression(fillerExpression, ExpressionOptions.CaseInsensitiveStringComparer);
int chapterNum = index + 1;
double sinceLastFiller = chapterPoint.TotalSeconds - lastFiller;
int matchedPoints = matches;
@ -45,6 +46,7 @@ public static class FillerExpression @@ -45,6 +46,7 @@ public static class FillerExpression
"matched_points" => matchedPoints,
"point" => chapterPoint.TotalSeconds,
"num" => chapterNum,
"title" => chapter.Title ?? string.Empty,
_ => e.Result
};
};

3
ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs

@ -692,7 +692,8 @@ public class TranscodingTests @@ -692,7 +692,8 @@ public class TranscodingTests
AudioFormat = FFmpegProfileAudioFormat.Aac,
DeinterlaceVideo = true,
BitDepth = profileBitDepth,
ScalingBehavior = scalingBehavior
ScalingBehavior = scalingBehavior,
NormalizeColors = profileNormalizeColors
},
StreamingMode = streamingMode,
SubtitleMode = subtitleMode

Loading…
Cancel
Save