Der Server wusste, was du zuletzt gesehen hast — und sonst nichts
Für „wie viel habe ich letzte Woche geschaut" gab es keine Daten. `UserItemState` ist eine Zeile je Titel: der letzte Stand. Wer einen Film zweimal sieht, überschreibt den ersten Durchgang; wann etwas lief, wusste der Server nur fürs jeweils letzte Mal. Nachträglich erfinden lässt sich das nicht. Jetzt gibt es ein Sehprotokoll: **eine Zeile je Nutzer, Tag und Titel.** Keine Sitzungen — die brauchen ein Ende, und das kommt oft nie an, wenn der Browser zugemacht oder das Telefon zugeklappt wird. Tagesbündel haben diesen Lebenslauf nicht, es wird nur addiert. **Gezählt wird, was wirklich lief.** Der Zuwachs zwischen zwei Meldungen, gedeckelt auf die Zeit, die dazwischen verstrichen ist (mal zwei als Toleranz für doppelte Geschwindigkeit und ausgefallene Meldungen). Sonst ergäbe ein Sprung von Minute 5 auf Minute 90 anderthalb Stunden Sehzeit in zehn Sekunden. Gemessen an fünf Meldungen im Abstand von elf Sekunden: 88 Sekunden statt 190. Dabei fiel ein zweiter Fall auf: war **gar keine** Zeit verstrichen, griff kein Deckel statt eines engen — eine frisch angelegte Zeile hätte ihre Anfangsstelle voll mitgezählt. Wer einen Film öffnet und sofort auf Minute fünf springt, hat keine fünf Minuten gesehen. Im Profil stehen drei Zahlen, und jede sagt, woher sie kommt: die aufgezeichnete Zeit fängt heute an, die Zahl gesehener Titel steht seit jeher fest. Dazu eine Wochenkurve, die häufigsten Genres und was zuletzt lief. **Jeder sieht nur sich selbst, auch ein Administrator** — es gibt keinen Weg, ein fremdes Konto zu meinen. Zwei Dinge auf dem Weg dorthin: **Eine stellbare Uhr für die Tests.** Zwei Minuten Film lassen sich nicht in fünf Millisekunden sehen, und das ist richtig so — prüfen liess es sich nur, indem die Zeit vergeht, ohne sie zu verbringen. **Ein Namenskonflikt im CSS.** Die leeren Wochenbalken trugen die Klasse `leer`, und die gehört längst der Fläche „hier ist nichts" — mitsamt 24 Pixeln Polster. Jeder leere Balken war dadurch 48 Pixel hoch, egal welche Höhe dastand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d8a1633cf4
commit
579a49b73b
@@ -0,0 +1,60 @@
|
||||
namespace Flaro.Core.Playback;
|
||||
|
||||
/// <summary>Wie lange an einem Tag an einem Titel gesehen wurde.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Warum es das überhaupt gibt.</b> <see cref="UserItemState"/> ist eine
|
||||
/// Zeile je Titel und Nutzer — der <i>letzte</i> Stand. Wer einen Film zweimal
|
||||
/// sieht, überschreibt den ersten Durchgang; wann etwas lief, weiß der Server
|
||||
/// nur für das jeweils letzte Mal. Für „wie viel habe ich letzte Woche
|
||||
/// geschaut" reicht das nicht, und raten kann man es nicht.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Warum Tagesbündel und keine Sitzungen.</b> Eine Sitzungstabelle braucht
|
||||
/// Anfang <i>und</i> Ende, und das Ende kommt oft nie an: der Browser wird
|
||||
/// zugemacht, das Telefon schläft ein. Dann stehen offene Sitzungen herum, die
|
||||
/// niemand schliesst, und jede Auswertung muss raten, was davon zählt.
|
||||
/// Tagesbündel haben diesen Lebenslauf nicht — es wird nur addiert. Für die
|
||||
/// Fragen, um die es geht (wie viel insgesamt, wie viel je Woche, was
|
||||
/// zuletzt), reicht das vollständig.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// <b>Es fängt bei null an.</b> Was vor dem Einbau lief, steht nicht darin —
|
||||
/// die Daten gibt es nicht und lassen sich nicht nachträglich erfinden. Was
|
||||
/// sofort vollständig ist, steht weiter in <see cref="UserItemState"/>:
|
||||
/// gesehen ja/nein, wie oft, wann zuletzt.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public sealed class Sehtag
|
||||
{
|
||||
public Guid Id { get; init; } = Guid.CreateVersion7();
|
||||
|
||||
public required Guid UserId { get; init; }
|
||||
|
||||
/// <summary>Entweder ein Film …</summary>
|
||||
public Guid? MovieId { get; init; }
|
||||
|
||||
/// <summary>… oder eine Folge. Nie beides, nie keines.</summary>
|
||||
public Guid? EpisodeId { get; init; }
|
||||
|
||||
/// <summary>Der Tag in der Zeitzone des Servers.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Ortszeit, nicht UTC.</b> Wer um halb eins nachts einen Film zu Ende
|
||||
/// sieht, hat das am Abend getan und nicht am nächsten Morgen — eine
|
||||
/// Wochenkurve nach UTC verschöbe jeden späten Abend auf den Folgetag.
|
||||
/// Der Server hat eine Zeitzone, und die ist die des Haushalts.
|
||||
/// </remarks>
|
||||
public required DateOnly Tag { get; init; }
|
||||
|
||||
/// <summary>Wie viele Sekunden an diesem Tag tatsächlich liefen.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Tatsächlich, nicht übersprungen.</b> Gezählt wird der Zuwachs
|
||||
/// zwischen zwei Meldungen, gedeckelt auf die Zeit, die dazwischen
|
||||
/// verstrichen ist. Ein Sprung von Minute 5 auf Minute 90 ergäbe sonst
|
||||
/// 85 Minuten Sehzeit in zehn Sekunden.
|
||||
/// </remarks>
|
||||
public double Sekunden { get; set; }
|
||||
}
|
||||
Reference in New Issue
Block a user