Marken suchen, ohne dass jemand danach fragt

Eine neue Folge bekommt keine Vorspann-Marke, wenn niemand den Knopf
drückt — und niemand drückt ihn, weil man nicht weiss, dass es etwas zu
drücken gibt. Der Scan ist der Augenblick, in dem neue Folgen entstehen;
jetzt hängt sich der Lauf dort hinten an.

Eine Serie nach der anderen, nicht alle zugleich: jede liest von jeder
ihrer Folgen ein Stück Ton, und zwanzig gleichzeitig wären zwanzig
Leseköpfe auf derselben Platte. Gerechnet wird nur, wo noch nichts
steht — beim ersten Mal über einen grossen Bestand dauert das
entsprechend, danach findet der Lauf nichts mehr zu tun.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-12 21:52:24 +02:00
co-authored by Claude Opus 5
parent 1d70be0f99
commit adca700c4a
2 changed files with 57 additions and 0 deletions
+49
View File
@@ -84,6 +84,55 @@ public sealed class Markenerkennung(
return true; return true;
} }
/// <summary>Nach einem Scan: jede Serie der Mediathek, der noch Marken fehlen.</summary>
///
/// <remarks>
/// <para>
/// <b>Hier, weil es sonst nie passiert.</b> Eine neue Folge bekommt
/// keine Marke, wenn niemand danach fragt — und niemand fragt, weil man
/// nicht weiss, dass es etwas zu fragen gibt. Der Scan ist der
/// Augenblick, in dem neue Folgen entstehen.
/// </para>
/// <para>
/// <b>Eine nach der anderen, nicht alle zugleich.</b> Jede liest von
/// jeder ihrer Folgen ein Stück Ton; zwanzig Serien gleichzeitig hiesse
/// zwanzig Leseköpfe auf derselben Platte. Beim ersten Mal über einen
/// grossen Bestand dauert das entsprechend — danach findet der Lauf
/// nichts mehr zu tun und ist in Sekunden vorbei.
/// </para>
/// </remarks>
public async Task NachScanAsync(Guid mediathek, CancellationToken ct = default)
{
using var raum = scopes.CreateScope();
var db = raum.ServiceProvider.GetRequiredService<FlaroContext>();
var leser = raum.ServiceProvider.GetRequiredService<Tonabdruckleser>();
if (!await leser.VorhandenAsync(ct)) return;
var serien = await db.Series.AsNoTracking()
.Where(x => x.LibraryId == mediathek)
.Select(x => x.Id)
.ToListAsync(ct);
if (serien.Count == 0) return;
log.LogInformation(
"Suche Vorspann und Abspann in {Anzahl} Serien — das kann dauern.", serien.Count);
foreach (var serie in serien)
{
if (ct.IsCancellationRequested) return;
if (!_laufend.TryAdd(serie, true)) continue;
try { await LaufenAsync(serie, erneut: false, ct); }
catch (Exception fehler)
{
log.LogWarning(fehler, "Markenerkennung für {Serie} abgebrochen.", serie);
}
finally { _laufend.TryRemove(serie, out _); }
}
log.LogInformation("Marken für Mediathek {Kennung} durch.", mediathek);
}
private async Task LaufenAsync(Guid serieId, bool erneut, CancellationToken ct) private async Task LaufenAsync(Guid serieId, bool erneut, CancellationToken ct)
{ {
using var raum = scopes.CreateScope(); using var raum = scopes.CreateScope();
+8
View File
@@ -175,6 +175,14 @@ public sealed class ScanCoordinator(
_ = bereich.ServiceProvider.GetRequiredService<Kratzplatzpflege>() _ = bereich.ServiceProvider.GetRequiredService<Kratzplatzpflege>()
.LaufenAsync(Kratzplatzpflege.Anlass.NachDemScan); .LaufenAsync(Kratzplatzpflege.Anlass.NachDemScan);
// **Und zuletzt die Marken.** Ein Scan ist der Augenblick, in
// dem neue Folgen dazukommen — und eine Folge ohne Vorspann-
// Marke bekommt nie eine, wenn niemand danach fragt. Der Lauf
// hängt sich hinten an und nimmt sich Serie für Serie vor;
// gerechnet wird nur, wo noch nichts steht.
_ = bereich.ServiceProvider.GetRequiredService<Markenerkennung>()
.NachScanAsync(libraryId);
_zustand[libraryId] = _zustand[libraryId] with _zustand[libraryId] = _zustand[libraryId] with
{ {
Status = "finished", Status = "finished",