using System.Collections.Concurrent; namespace Flaro.Api; /// Was gerade läuft, je Gerät. /// /// /// /// Im Arbeitsspeicher, nicht in der Datenbank. „Wer schaut gerade /// was" ist eine Frage über den Augenblick; sie zu speichern hiesse, nach /// einem Neustart Wiedergaben anzuzeigen, die es nicht mehr gibt. Fällt der /// Container aus, ist die Antwort ohnehin „niemand". /// /// /// Abgeleitet aus den Fortschrittsmeldungen, nicht aus einer eigenen /// Anmeldung am Spieler. Jeder Client meldet beim Abspielen ohnehin alle /// paar Sekunden, wo er steht — wer das als Lebenszeichen liest, braucht /// kein zweites Verfahren, das ein Client vergessen könnte. Bleibt die /// Meldung aus, läuft der Eintrag von selbst ab. /// /// public sealed class PlaybackSessions(TimeProvider clock) { /// Wie lange eine Wiedergabe ohne Meldung als laufend gilt. /// /// /// Die Clients melden alle zehn Sekunden. Eine Minute lässt Raum für /// einen Aussetzer, ohne dass eine längst beendete Wiedergabe noch /// minutenlang im Dashboard steht. /// public static readonly TimeSpan Lebensdauer = TimeSpan.FromMinutes(1); private readonly ConcurrentDictionary _laufend = new(); public sealed record Wiedergabe( string DeviceKey, Guid UserId, string UserName, Guid ItemId, string Title, string? SubTitle, string? PosterImage, TimeSpan Position, TimeSpan? Runtime, string DeviceName, DateTimeOffset LastSeenAt); /// Hält fest, dass dieses Gerät gerade etwas abspielt. public void Melden(Wiedergabe wiedergabe) => _laufend[wiedergabe.DeviceKey] = wiedergabe; /// Nimmt eine Wiedergabe heraus — beim Beenden. public void Beenden(string deviceKey) => _laufend.TryRemove(deviceKey, out _); /// Was gerade läuft. Abgelaufenes fällt dabei heraus. public IReadOnlyList Laufende() { var grenze = clock.GetUtcNow() - Lebensdauer; // Beiläufig aufräumen. Ein eigener Hintergrunddienst für eine // Handvoll Einträge wäre Aufwand ohne Gegenwert. foreach (var (schluessel, eintrag) in _laufend) { if (eintrag.LastSeenAt < grenze) _laufend.TryRemove(schluessel, out _); } return _laufend.Values.OrderBy(w => w.UserName).ThenBy(w => w.Title).ToList(); } }