diff --git a/server/src/Flaro.Api/Markenerkennung.cs b/server/src/Flaro.Api/Markenerkennung.cs
index 1b50c93..835a127 100644
--- a/server/src/Flaro.Api/Markenerkennung.cs
+++ b/server/src/Flaro.Api/Markenerkennung.cs
@@ -64,6 +64,21 @@ public sealed class Markenerkennung(
///
public const double Hoechstlaenge = 5 * 60;
+ /// Dasselbe für den Abspann — und deutlich grosszügiger.
+ ///
+ ///
+ /// Fünf Minuten sind für einen Abspann keine Obergrenze, sondern
+ /// die Regel. Eine deutsche Serienproduktion listet Sprecher,
+ /// Synchronregie und Studio hinterher; sechs Minuten kommen vor. Mit
+ /// derselben Schranke wie beim Vorspann wurde der Fund gemacht und
+ /// gleich wieder weggeworfen — gefunden, verworfen, und am Ende stand
+ /// kein Knopf da, obwohl die Erkennung gearbeitet hatte.
+ ///
+ /// Weiter als das Suchfenster kann eine Marke ohnehin nicht reichen;
+ /// mehr als dieses Fenster zu erlauben hiesse nichts zu erlauben.
+ ///
+ public const double Hoechstabspann = Abspannfenster;
+
/// Welche Staffeln gerade abgetastet werden.
private readonly ConcurrentDictionary _laufend = new();
@@ -325,9 +340,11 @@ public sealed class Markenerkennung(
// vermerken hiesse, ihn beim nächsten Scan zu wiederholen.
await VermerkenAsync(db, serieId, staffel.Key, art, geordnet.Count, ct);
+ var grenze = art == Markenart.Intro ? Hoechstlaenge : Hoechstabspann;
+
foreach (var (folgenId, stelle) in treffer)
{
- if (stelle.Bis - stelle.Von > Hoechstlaenge) continue;
+ if (stelle.Bis - stelle.Von > grenze) continue;
// **Die Zeiten liegen im Fenster, nicht in der Folge.**
// Beim Abspann fängt das Fenster zehn Minuten vor dem
diff --git a/server/src/Flaro.Api/Streamkoordinator.cs b/server/src/Flaro.Api/Streamkoordinator.cs
index 27b336e..32438a5 100644
--- a/server/src/Flaro.Api/Streamkoordinator.cs
+++ b/server/src/Flaro.Api/Streamkoordinator.cs
@@ -231,7 +231,7 @@ public sealed class Streamkoordinator(
if (eintrag.Sitzung.Gesehen > grenze) continue;
if (!_sitzungen.TryRemove(kennung, out var weg)) continue;
- log.LogInformation("Strom {Kennung} wurde seit {Geduld} nicht abgeholt — beendet.",
+ log.LogInformation("Stream {Kennung} wurde seit {Geduld} nicht abgeholt — beendet.",
kennung, Geduld);
Abraeumen(weg);
}
@@ -396,7 +396,7 @@ public sealed class Streamkoordinator(
// eine Spur.
if (text.Trim() is { Length: > 0 } warnungen)
{
- log.LogWarning("Strom {Kennung}: ffmpeg meldet {Warnungen}",
+ log.LogWarning("Stream {Kennung}: ffmpeg meldet {Warnungen}",
sitzung.Kennung, warnungen);
}
@@ -434,7 +434,7 @@ public sealed class Streamkoordinator(
}
catch (IOException fehler)
{
- log.LogWarning(fehler, "Der Ordner zu Strom {Kennung} blieb liegen.", eintrag.Sitzung.Kennung);
+ log.LogWarning(fehler, "Der Ordner zu Stream {Kennung} blieb liegen.", eintrag.Sitzung.Kennung);
}
}
@@ -458,7 +458,7 @@ public sealed class Streamkoordinator(
try { Directory.Delete(Wurzel, recursive: true); }
catch (IOException fehler)
{
- log.LogDebug(fehler, "Der Stromordner {Pfad} liess sich nicht leeren.", Wurzel);
+ log.LogDebug(fehler, "Der Streamordner {Pfad} liess sich nicht leeren.", Wurzel);
return 0;
}
diff --git a/web/app/src/spieler.ts b/web/app/src/spieler.ts
index b744bea..9a71991 100644
--- a/web/app/src/spieler.ts
+++ b/web/app/src/spieler.ts
@@ -1284,7 +1284,7 @@ export function spieler(anfrage: Spieleranfrage): void {
function hlsfehler(art: string, arten: { NETWORK_ERROR: string; MEDIA_ERROR: string }): string {
if (art === arten.NETWORK_ERROR) {
- return 'Der Strom ist abgerissen. Entweder war die Verbindung weg, '
+ return 'Der Stream ist abgerissen. Entweder war die Verbindung weg, '
+ 'oder der Server hat die Sitzung aufgeräumt — fang die Wiedergabe neu an.';
}
if (art === arten.MEDIA_ERROR) {
diff --git a/web/app/src/views/detail.ts b/web/app/src/views/detail.ts
index 8a5208c..625f00a 100644
--- a/web/app/src/views/detail.ts
+++ b/web/app/src/views/detail.ts
@@ -1085,6 +1085,20 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
/** Und wie sie heisst — für die Zeile über dem Spieler. */
let serienname: string | undefined;
+ /** Was die Erkennung bei dieser Folge gefunden hat — nur für Verwalter.
+ *
+ * **Ohne das bleibt „es kommt kein Knopf" eine Vermutung.** Ob die Marke
+ * gar nicht gefunden wurde oder bloss nicht angezeigt wird, ist von
+ * aussen nicht zu unterscheiden — und beides hat eine andere Ursache.
+ * Einmal nachsehen zu können erspart eine Runde Raten. */
+ function markenwort(marken: Folge['markers']): string {
+ if (!istAdmin()) return '';
+ if (!marken || marken.length === 0) return 'keine Marken';
+ return marken
+ .map(m => m.kind === 'intro' ? 'Vorspann' : 'Abspann')
+ .join(' + ');
+ }
+
function folgenblock(folge: Folge): HTMLElement {
const block = document.createElement('div');
block.className = 'einstellung';
@@ -1095,9 +1109,11 @@ export function detailseite(art: Art, id: string, zurueck: () => void): HTMLElem
: folge.state && folge.state.positionSeconds > 0
? `noch ${Math.round(((folge.state.runtimeSeconds ?? 0) - folge.state.positionSeconds) / 60)} Min`
: '';
+ const marken = markenwort(folge.markers);
+ const daneben = [nebenbei, marken].filter(Boolean).join(' · ');
zeile.innerHTML =
`${String(folge.episode).padStart(2, '0')} · ${esc(folge.title ?? 'Ohne Titel')}`
- + (nebenbei ? `${esc(nebenbei)}` : '');
+ + (daneben ? `${esc(daneben)}` : '');
const wie = `S${folge.season} · F${folge.episode}`
+ (folge.title ? ` — ${folge.title}` : '');