Phase 5.2.4: Tune HLS segmentation for HA recovery (#29)

* Initial plan

* Phase 5.2.4: Tune HLS behavior for recovery with configurable segment parameters

Co-authored-by: ZoltyMat <177592743+ZoltyMat@users.noreply.github.com>

* fix: SA1516 — add blank line between MakeLiveStreamKey and Clone helpers

StyleCop SA1516 requires elements to be separated by blank lines.
Missing blank line at line 370 caused build failure in Phase 5 tests.

Closes #28

* fix: SA1516 — blank line in InMemoryTranscodeSessionStore between helpers

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: ZoltyMat <177592743+ZoltyMat@users.noreply.github.com>
Co-authored-by: mat <mstrommen@gmail.com>
This commit is contained in:
Copilot
2026-03-10 02:36:54 -04:00
committed by mat
parent e71efc3f97
commit fe9ea18918
6 changed files with 156 additions and 8 deletions
@@ -94,6 +94,54 @@ namespace Jellyfin.Api.Tests.Controllers
Assert.Null(liveSession);
}
/// <summary>
/// Segment-length selection: when the play-session has an active entry in the store
/// (HA mode is active), the recovery segment length should be preferred over the normal one.
/// </summary>
[Fact]
[Trait("Category", "UnitTest")]
public async Task SegmentLength_UsesRecoveryValue_WhenHaModeIsActive()
{
const int normalSegmentLength = 6;
const int recoverySegmentLength = 2;
var store = new HaTestSessionStore();
var session = CreateSession("ha-session-4", "pod-a", DateTime.UtcNow.AddMinutes(5));
await store.SetAsync(session);
// Simulate the controller's HA-mode check: if the session is in the store, HA mode is active.
var existingSession = await store.TryGetAsync("ha-session-4");
var isHaMode = existingSession is not null;
var effectiveSegmentLength = isHaMode ? recoverySegmentLength : normalSegmentLength;
Assert.True(isHaMode, "Session should be found in the store, activating HA mode.");
Assert.Equal(recoverySegmentLength, effectiveSegmentLength);
}
/// <summary>
/// Segment-length selection: when no entry exists in the store for the play-session
/// (HA mode inactive), the normal segment length should be used.
/// </summary>
[Fact]
[Trait("Category", "UnitTest")]
public async Task SegmentLength_UsesNormalValue_WhenHaModeIsInactive()
{
const int normalSegmentLength = 6;
const int recoverySegmentLength = 2;
var store = new HaTestSessionStore();
// No session registered – HA mode is inactive.
var existingSession = await store.TryGetAsync("nonexistent-session");
var isHaMode = existingSession is not null;
var effectiveSegmentLength = isHaMode ? recoverySegmentLength : normalSegmentLength;
Assert.False(isHaMode, "No session in the store means HA mode should be inactive.");
Assert.Equal(normalSegmentLength, effectiveSegmentLength);
}
/// <summary>
/// Minimal in-memory <see cref="ITranscodeSessionStore"/> used within this test class
/// to avoid a cross-project reference to Jellyfin.MediaEncoding.Tests.
@@ -153,6 +153,7 @@ public sealed class InMemoryTranscodeSessionStore : ITranscodeSessionStore
private static string MakeLiveStreamKey(string liveStreamId, string sessionIdOrPlaySessionId)
=> liveStreamId + "\x00" + sessionIdOrPlaySessionId;
private static TranscodeSession Clone(TranscodeSession source)
=> new TranscodeSession
{
@@ -367,6 +367,7 @@ public class RedisTranscodeSessionStoreTests
private static string MakeLiveStreamKey(string liveStreamId, string sessionIdOrPlaySessionId)
=> liveStreamId + "\x00" + sessionIdOrPlaySessionId;
private static TranscodeSession Clone(TranscodeSession source)
=> new TranscodeSession
{