using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.MediaEncoding; namespace Jellyfin.MediaEncoding.Tests.Fakes; /// /// Thread-safe, in-memory implementation of for use in unit tests. /// public sealed class InMemoryTranscodeSessionStore : ITranscodeSessionStore { /// /// The duration added to when a lease is renewed or first claimed. /// public static readonly TimeSpan DefaultLeaseDuration = TimeSpan.FromSeconds(30); private readonly Dictionary _sessions = new(StringComparer.OrdinalIgnoreCase); private readonly Lock _lock = new(); /// public Task TryGetAsync(string playSessionId, CancellationToken cancellationToken = default) { lock (_lock) { if (_sessions.TryGetValue(playSessionId, out var session) && session.LeaseExpiresUtc > DateTime.UtcNow) { return Task.FromResult(Clone(session)); } return Task.FromResult(null); } } /// public Task TryTakeoverAsync(string playSessionId, string claimingPod, CancellationToken cancellationToken = default) { lock (_lock) { if (!_sessions.TryGetValue(playSessionId, out var session)) { return Task.FromResult(false); } if (session.LeaseExpiresUtc > DateTime.UtcNow) { // Another pod's lease is still valid – takeover not permitted. return Task.FromResult(false); } // Lease has expired – claim it atomically. session.OwnerPod = claimingPod; session.LeaseExpiresUtc = DateTime.UtcNow.Add(DefaultLeaseDuration); return Task.FromResult(true); } } /// public Task SetAsync(TranscodeSession session, CancellationToken cancellationToken = default) { lock (_lock) { _sessions[session.PlaySessionId] = session; } return Task.CompletedTask; } /// public Task RenewLeaseAsync(string playSessionId, CancellationToken cancellationToken = default) { lock (_lock) { if (_sessions.TryGetValue(playSessionId, out var session)) { session.LeaseExpiresUtc = DateTime.UtcNow.Add(DefaultLeaseDuration); } } return Task.CompletedTask; } /// public Task DeleteAsync(string playSessionId, CancellationToken cancellationToken = default) { lock (_lock) { _sessions.Remove(playSessionId); } return Task.CompletedTask; } private static TranscodeSession Clone(TranscodeSession source) => new TranscodeSession { PlaySessionId = source.PlaySessionId, OwnerPod = source.OwnerPod, LeaseExpiresUtc = source.LeaseExpiresUtc, ManifestPath = source.ManifestPath, SegmentPathPrefix = source.SegmentPathPrefix, MediaSourceId = source.MediaSourceId, LastCompletedSegmentIndex = source.LastCompletedSegmentIndex, LastDurablePlaybackOffset = source.LastDurablePlaybackOffset, }; }