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}` : '');