„No such file or directory" über Dateien, die da waren
Im Protokoll stand für jede einzelne Folge, fpcalc könne die Datei nicht öffnen — während der Spieler dieselbe Datei im selben Augenblick umpackte. Null Marken, und der Grund stand nicht da. `-ts` ist bei fpcalc kein Zeitstempel, sondern ein Schalter: „gib Zeitstempel aus". Die Sekundenzahl dahinter las fpcalc als zweiten Dateinamen, fand ihn nicht und brach ab, bevor es die echte Datei überhaupt ansah. Nachgemessen am Binärprogramm aus dem Abbild: einen Startpunkt gibt es bei fpcalc gar nicht. `-length` begrenzt nur die Dauer vom Anfang an — für den Abspann braucht es aber das Ende. Also einmal die ganze Spur je Folge, und Vorspann wie Abspann sind danach zwei Ausschnitte aus demselben Abdruck. Das ist nicht nur richtig, sondern billiger: eine Folge von 45 Minuten kostet 1,6 Sekunden und ergibt 21 786 Werte — gegenüber zwei Ausschnitten, die zusammen mehr gekostet hätten und nie funktionierten. Die Rate von 0,1239 Sekunden je Wert steht jetzt als Prüfung daneben. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
2c535e6f47
commit
fd79fb4022
@@ -166,28 +166,39 @@ public sealed class Markenerkennung(
|
||||
var geordnet = staffel.OrderBy(f => f.EpisodeNumber).ToList();
|
||||
if (geordnet.Count < 2) continue;
|
||||
|
||||
foreach (var art in new[] { Markenart.Intro, Markenart.Abspann })
|
||||
{
|
||||
// Schon gefunden? Dann nicht noch einmal rechnen — es sei
|
||||
// denn, jemand verlangt es ausdrücklich.
|
||||
if (!erneut && geordnet.All(f =>
|
||||
vorhanden.Any(v => v.EpisodeId == f.Id && v.Art == art)))
|
||||
continue;
|
||||
// **Einmal lesen, zweimal schneiden.** fpcalc kann nicht
|
||||
// springen — es liest immer von vorn. Also die ganze Spur je
|
||||
// Folge, und Vorspann wie Abspann sind danach zwei Ausschnitte
|
||||
// aus demselben Abdruck. Das ist nicht nur richtig, sondern
|
||||
// billiger als zweimal lesen.
|
||||
var fehlt = new[] { Markenart.Intro, Markenart.Abspann }
|
||||
.Where(art => erneut || !geordnet.All(f =>
|
||||
vorhanden.Any(v => v.EpisodeId == f.Id && v.Art == art)))
|
||||
.ToList();
|
||||
if (fehlt.Count == 0) continue;
|
||||
|
||||
var ganze = new Dictionary<Guid, IReadOnlyList<uint>>();
|
||||
foreach (var folge in geordnet)
|
||||
{
|
||||
var abdruck = await leser.LesenAsync(folge.Path, ct);
|
||||
if (abdruck.Count > 0) ganze[folge.Id] = abdruck;
|
||||
}
|
||||
if (ganze.Count < 2) continue;
|
||||
|
||||
foreach (var art in fehlt)
|
||||
{
|
||||
var abdruecke = new List<(Guid Schlüssel, IReadOnlyList<uint> Abdruck)>();
|
||||
var fensteranfang = new Dictionary<Guid, double>();
|
||||
|
||||
foreach (var folge in geordnet)
|
||||
{
|
||||
var laufzeit = folge.Duration?.TotalSeconds ?? 0;
|
||||
var (ab, dauer) = Fenster(art, laufzeit);
|
||||
if (dauer <= 0) continue;
|
||||
if (!ganze.TryGetValue(folge.Id, out var ganz)) continue;
|
||||
|
||||
var abdruck = await leser.LesenAsync(folge.Path, ab, dauer, ct);
|
||||
if (abdruck.Count == 0) continue;
|
||||
var (vonIndex, anzahl) = Ausschnitt(art, ganz.Count);
|
||||
if (anzahl < 2) continue;
|
||||
|
||||
abdruecke.Add((folge.Id, abdruck));
|
||||
fensteranfang[folge.Id] = ab;
|
||||
abdruecke.Add((folge.Id, ganz.Skip(vonIndex).Take(anzahl).ToList()));
|
||||
fensteranfang[folge.Id] = vonIndex * Tonabdruckleser.SekundenJeAbdruck;
|
||||
}
|
||||
|
||||
if (abdruecke.Count < 2) continue;
|
||||
@@ -215,15 +226,21 @@ public sealed class Markenerkennung(
|
||||
log.LogInformation("Markenerkennung für {Serie}: {Anzahl} Marken.", serieId, gefunden);
|
||||
}
|
||||
|
||||
/// <summary>Welcher Ausschnitt der Folge abgetastet wird.</summary>
|
||||
private static (double Ab, double Dauer) Fenster(Markenart art, double laufzeit)
|
||||
/// <summary>Welcher Ausschnitt des Abdrucks abgetastet wird.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// In Werten, nicht in Sekunden: der Abdruck ist eine Reihe, und
|
||||
/// geschnitten wird an einer Stelle darin. Die Laufzeit steht nicht mehr
|
||||
/// in der Rechnung — die Länge des Abdrucks ist sie.
|
||||
/// </remarks>
|
||||
private static (int Von, int Anzahl) Ausschnitt(Markenart art, int werte)
|
||||
{
|
||||
if (art == Markenart.Intro) return (0, laufzeit > 0 ? Math.Min(Introfenster, laufzeit) : Introfenster);
|
||||
var fenster = (int)Math.Round(
|
||||
(art == Markenart.Intro ? Introfenster : Abspannfenster)
|
||||
/ Tonabdruckleser.SekundenJeAbdruck);
|
||||
|
||||
// Ohne bekannte Laufzeit lässt sich das Ende nicht finden.
|
||||
if (laufzeit <= 0) return (0, 0);
|
||||
var ab = Math.Max(0, laufzeit - Abspannfenster);
|
||||
return (ab, laufzeit - ab);
|
||||
if (fenster >= werte) return (0, werte);
|
||||
return art == Markenart.Intro ? (0, fenster) : (werte - fenster, fenster);
|
||||
}
|
||||
|
||||
private async Task<bool> SchreibenAsync(
|
||||
|
||||
Reference in New Issue
Block a user