Eine Sekunde ist kein Anfang
Drei Zahlen standen als Konstante im Quelltext und bestimmten, wie sich der Server anfühlt. Jetzt stehen sie in der Verwaltung, unter „Wiedergabe". **Weiterschauen ab.** Gemessen: `BelongsInContinueWatching` verlangte nur `Position.Ticks > 0` — ein Tick genügte. Wer im Aufmacher eine Sekunde hineinschaute, fand den Titel danach in „Fortsetzen". Jetzt gibt es eine Untergrenze, voreingestellt auf 1½ Minuten. Was sich dabei **nicht** ändert: die Stelle wird weiter gemerkt. Das ist der Unterschied zu Jellyfin, um dessentwillen es diesen Server gibt — die Schwelle entscheidet, ob der Titel in der Reihe steht, nicht ob man ihn wiederfindet. Nachgemessen: nach dem Umstellen ist „Heat" aus der Reihe verschwunden und steht weiterhin bei Sekunde 1. **Gesehen ab.** War 90 % als `const`, ist jetzt 85/90/95. **Umkodieren.** `-preset veryfast -crf 21` und VAAPI `-qp 23` waren fest verdrahtet, ohne Einstellung und ohne Umgebungsvariable. Drei Stufen: Qualität (medium/19, qp 21), Ausgewogen (wie bisher), Tempo (ultrafast/23, qp 26). Mit dem Warnsatz daneben, und der ist ernst gemeint: was in Echtzeit laufen muss, darf nicht langsamer rechnen als es abspielt. **Und das Vorhandene wird neu bewertet.** `InProgress` ist eine gespeicherte Spalte — die Bruchrechnung lässt sich nicht nach SQL übersetzen. Eine neue Schwelle wirkte sonst nur auf künftige Meldungen. Der Endpunkt läuft deshalb einmal in Stapeln durch und meldet zurück, wie viele Zeilen das umgeworfen hat: „Übernommen — 2 Einträge wurden neu bewertet." Ohne diesen Satz änderte sich die Startseite scheinbar von selbst. `ProgressRule` bleibt ein reiner Typ: die Regeln kommen als Wert herein. Nebenbei zieht der doppelte `zeile()`-Bauer nach `teile.ts` — er stand zweimal wortgleich da. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
1e4a231d1b
commit
9622da0284
@@ -0,0 +1,83 @@
|
||||
using Flaro.Core.Delivery;
|
||||
using Flaro.Core.Playback;
|
||||
using Flaro.Data;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace Flaro.Api;
|
||||
|
||||
/// <summary>Holt die Wiedergabe-Einstellungen aus der Datenbank.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Dasselbe Muster wie <see cref="DatabaseTmdbSettings"/>:</b> je Anfrage
|
||||
/// einmal gelesen und dann gemerkt. Eine Fortschrittsmeldung kommt alle zehn
|
||||
/// Sekunden und von jedem Gerät; dreimal dieselbe Zeile je Meldung zu lesen
|
||||
/// wäre Arbeit für nichts.
|
||||
///
|
||||
/// <b>Serverweit, nicht je Nutzer.</b> Der Transcoder gehört der Maschine —
|
||||
/// es gibt eine CPU und eine Entscheidung. Die beiden Schwellen könnten je
|
||||
/// Nutzer gelten; sie tun es nicht, weil zwei Wahrheiten darüber, was
|
||||
/// „angefangen" heisst, mehr kosten als sie einbringen.
|
||||
/// </remarks>
|
||||
public sealed class Wiedergabeeinstellungen(FlaroContext db)
|
||||
{
|
||||
public const string FortsetzenAbName = "wiedergabe.fortsetzen.abSekunden";
|
||||
public const string GesehenAnteilName = "wiedergabe.gesehen.anteil";
|
||||
public const string TranscoderName = "wiedergabe.transcoder.stufe";
|
||||
|
||||
private Wiedergaberegeln? _regeln;
|
||||
private Kodierstufe? _stufe;
|
||||
|
||||
/// <summary>Wann ein Titel angefangen ist und wann er gesehen ist.</summary>
|
||||
public async Task<Wiedergaberegeln> RegelnAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (_regeln is { } fertig) return fertig;
|
||||
await LesenAsync(ct);
|
||||
return _regeln!.Value;
|
||||
}
|
||||
|
||||
/// <summary>Wie der Transcoder zwischen Bild und Tempo abwägt.</summary>
|
||||
public async Task<Kodierstufe> StufeAsync(CancellationToken ct = default)
|
||||
{
|
||||
if (_stufe is { } fertig) return fertig;
|
||||
await LesenAsync(ct);
|
||||
return _stufe!.Value;
|
||||
}
|
||||
|
||||
private async Task LesenAsync(CancellationToken ct)
|
||||
{
|
||||
var zeilen = await db.Settings
|
||||
.AsNoTracking()
|
||||
.Where(s => s.Key == FortsetzenAbName
|
||||
|| s.Key == GesehenAnteilName
|
||||
|| s.Key == TranscoderName)
|
||||
.ToDictionaryAsync(s => s.Key, s => s.Value, ct);
|
||||
|
||||
_regeln = Lesen(zeilen.GetValueOrDefault(FortsetzenAbName),
|
||||
zeilen.GetValueOrDefault(GesehenAnteilName));
|
||||
_stufe = Kodierstufen.Lesen(zeilen.GetValueOrDefault(TranscoderName));
|
||||
}
|
||||
|
||||
/// <summary>Macht aus zwei gespeicherten Zeichenketten geprüfte Regeln.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <b>Ohne Ortsangabe gerechnet</b> — <see cref="double.TryParse(string?,
|
||||
/// IFormatProvider?, out double)"/> mit <see
|
||||
/// cref="System.Globalization.CultureInfo.InvariantCulture"/>: in der
|
||||
/// Datenbank steht ein Punkt, und auf einem Server mit deutscher
|
||||
/// Spracheinstellung läse ein Komma-Parser aus „0.9" eine Neun.
|
||||
/// </remarks>
|
||||
public static Wiedergaberegeln Lesen(string? abSekunden, string? anteil)
|
||||
{
|
||||
var regeln = Wiedergaberegeln.Vorgabe;
|
||||
|
||||
if (double.TryParse(abSekunden, System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var sekunden))
|
||||
regeln = regeln with { FortsetzenAb = TimeSpan.FromSeconds(sekunden) };
|
||||
|
||||
if (double.TryParse(anteil, System.Globalization.NumberStyles.Float,
|
||||
System.Globalization.CultureInfo.InvariantCulture, out var wert))
|
||||
regeln = regeln with { GesehenAnteil = wert };
|
||||
|
||||
return regeln.Geprueft();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user