Kratzplatz, Markenerkennung, Tonabdruck — erfundene Wörter für Fachbegriffe
„Scratch disk" wörtlich zu übersetzen ist derselbe Fehler wie „Wachhund"
für Watchdog und „Keks" für Cookie. Und „Marke" ist kein Fachbegriff für
das, was gemeint ist: Jellyfin nennt diese Abschnitte Media Segments, und
das ist auch das Wort, in dem sich darüber reden lässt.
Kratzplatz* → Transcode* (was es ist, steht in FLARO_TRANSCODE)
Marke, Marken → Segment, Segmente
Markenerkennung → Segmenterkennung
Markenart/-suche → Segmentart/-suche
Tonabdruck → Fingerprint (Chromaprint nennt es so)
Tonabgleich → Fingerprintabgleich
Schlüsselbild → Keyframe
Bildabtaster → Frameabtaster
Gewöhnliches Deutsch bleibt gewöhnliches Deutsch: Wartung, Freigabe,
Meldung, Abspann, Vorspann. Das war nie das Problem.
**Was sich nicht ändert, und zwar mit Absicht.** Die drei
Einstellungsschlüssel `wartung.kratzplatz.paket.*` bleiben, wie sie sind —
sie stehen in der Datenbank, und ein neuer Name hiesse, dass die Werte
des Nutzers stillschweigend auf die Vorgabe zurückfallen. Dasselbe für
die Endpunktpfade `/marken`: sie sind nicht sichtbar, und ein Umbenennen
wäre Diff ohne Gewinn.
**Die Migration ist von Hand geschrieben.** `dotnet ef` hat ein Gerüst
erzeugt, das beide Tabellen löscht und neu anlegt — es sieht eine
verschwundene und eine neue Tabelle, nicht eine umbenannte. Damit wäre
jedes erkannte Segment und jeder Vermerk „hier wurde schon gesucht" weg
gewesen. `RenameTable` und `RenameIndex` tun dasselbe, ohne eine Zeile
anzufassen; nachgemessen an einer echten SQLite-Datei: Zeilen vorher 1
und 1, nachher 1 und 1, Inhalt unverändert.
Nachgezogen: in `bilder.ts` und `main.ts` heisst „marke" ein Häkchen und
das Logo — die blieben unberührt. Und „Segment" ist sächlich, was eine
Blindersetzung nicht weiss: „eine Segment" stand danach an vierzehn
Stellen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
32f592b83e
commit
ed13d5fa43
+16
-16
@@ -8,41 +8,41 @@ namespace Flaro.Media.Tests;
|
||||
/// <b>Ohne das Programm selbst.</b> Ob Chromaprint rechnen kann, ist nicht
|
||||
/// unsere Frage; ob wir seine Ausgabe richtig verstehen, schon. Die Zahlen
|
||||
/// kommen mit Vorzeichen, gemeint sind die Bitmuster — wer das verwechselt,
|
||||
/// bekommt einen Abdruck, der nie zu einem zweiten passt.
|
||||
/// bekommt einen Fingerprint, der nie zu einem zweiten passt.
|
||||
/// </remarks>
|
||||
public class TonabdruckleserTests
|
||||
public class FingerprintleserTests
|
||||
{
|
||||
[Test]
|
||||
public void Die_Zahlen_werden_als_Bitmuster_gelesen()
|
||||
{
|
||||
var abdruck = Tonabdruckleser.Lesen("DURATION=120\nFINGERPRINT=1,-1,2147483647,-2147483648\n");
|
||||
var fingerprint = Fingerprintleser.Lesen("DURATION=120\nFINGERPRINT=1,-1,2147483647,-2147483648\n");
|
||||
|
||||
Assert.That(abdruck, Is.EqualTo(new uint[] { 1u, 0xFFFFFFFFu, 0x7FFFFFFFu, 0x80000000u }));
|
||||
Assert.That(fingerprint, Is.EqualTo(new uint[] { 1u, 0xFFFFFFFFu, 0x7FFFFFFFu, 0x80000000u }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Ohne_Abdruckzeile_kommt_nichts()
|
||||
public void Ohne_Fingerprintzeile_kommt_nichts()
|
||||
{
|
||||
Assert.That(Tonabdruckleser.Lesen("DURATION=120\n"), Is.Empty);
|
||||
Assert.That(Fingerprintleser.Lesen("DURATION=120\n"), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Eine_leere_Zeile_ergibt_keinen_Abdruck()
|
||||
public void Eine_leere_Zeile_ergibt_keinen_Fingerprint()
|
||||
{
|
||||
Assert.That(Tonabdruckleser.Lesen("FINGERPRINT=\n"), Is.Empty);
|
||||
Assert.That(Fingerprintleser.Lesen("FINGERPRINT=\n"), Is.Empty);
|
||||
}
|
||||
|
||||
/// <summary>Eine Zeitscheibe ist rund 124 Millisekunden.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Daran hängt jede Zeitangabe, die aus einem Abdruck entsteht. Stimmt
|
||||
/// die Zahl nicht, liegt jede Marke um denselben Faktor daneben — und
|
||||
/// Daran hängt jede Zeitangabe, die aus einem Fingerprint entsteht. Stimmt
|
||||
/// die Zahl nicht, liegt jedes Segment um denselben Faktor daneben — und
|
||||
/// das sähe aus wie ein Fehler in der Erkennung.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Eine_Zeitscheibe_dauert_124_Millisekunden()
|
||||
{
|
||||
Assert.That(Tonabdruckleser.SekundenJeAbdruck, Is.EqualTo(0.1238).Within(0.0005));
|
||||
Assert.That(Fingerprintleser.SekundenJeFingerprint, Is.EqualTo(0.1238).Within(0.0005));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,27 +59,27 @@ public class TonabdruckleserTests
|
||||
/// Sekunden: <c>-length 0</c> ergibt 21 786 Werte, also 0,1239 Sekunden je
|
||||
/// Wert; <c>-length 120</c> bricht nach 948 Werten ab.
|
||||
/// </remarks>
|
||||
public class TonabdruckRateTests
|
||||
public class FingerprintRateTests
|
||||
{
|
||||
[Test]
|
||||
public void Die_Rate_stimmt_mit_der_Messung_ueberein()
|
||||
{
|
||||
// 21 786 Werte für 2700 Sekunden.
|
||||
var gemessen = 2700.0 / 21786;
|
||||
Assert.That(Math.Abs(gemessen - Tonabdruckleser.SekundenJeAbdruck), Is.LessThan(0.0005));
|
||||
Assert.That(Math.Abs(gemessen - Fingerprintleser.SekundenJeFingerprint), Is.LessThan(0.0005));
|
||||
}
|
||||
|
||||
/// <summary>Eine Stunde Abdruck passt in eine überschaubare Reihe.</summary>
|
||||
/// <summary>Eine Stunde Fingerprint passt in eine überschaubare Reihe.</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// Damit niemand auf die Idee kommt, den Abdruck einer ganzen Folge für
|
||||
/// Damit niemand auf die Idee kommt, den Fingerprint einer ganzen Folge für
|
||||
/// zu gross zu halten und wieder in Ausschnitten zu lesen: eine Stunde
|
||||
/// sind knapp 29 000 Werte, also 116 Kilobyte.
|
||||
/// </remarks>
|
||||
[Test]
|
||||
public void Eine_Stunde_sind_rund_29000_Werte()
|
||||
{
|
||||
var werte = (int)(3600 / Tonabdruckleser.SekundenJeAbdruck);
|
||||
var werte = (int)(3600 / Fingerprintleser.SekundenJeFingerprint);
|
||||
Assert.That(werte, Is.InRange(28500, 29500));
|
||||
}
|
||||
}
|
||||
+12
-12
@@ -6,7 +6,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
namespace Flaro.Media.Tests;
|
||||
|
||||
/// <summary>Die Ausgabe des Abtasters lesen — ohne ffmpeg.</summary>
|
||||
public class BildabtasterLesenTests
|
||||
public class FrameabtasterLesenTests
|
||||
{
|
||||
[Test]
|
||||
public void Zeit_und_Wert_werden_zu_Proben()
|
||||
@@ -19,12 +19,12 @@ public class BildabtasterLesenTests
|
||||
|
||||
""";
|
||||
|
||||
var proben = Bildabtaster.Lesen(ausgabe, versatz: 60);
|
||||
var proben = Frameabtaster.Lesen(ausgabe, versatz: 60);
|
||||
|
||||
Assert.That(proben, Is.EqualTo(new[]
|
||||
{
|
||||
new Bildprobe(62.5, 3),
|
||||
new Bildprobe(65, 84),
|
||||
new Frameprobe(62.5, 3),
|
||||
new Frameprobe(65, 84),
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -38,13 +38,13 @@ public class BildabtasterLesenTests
|
||||
[Test]
|
||||
public void Ein_Wert_ohne_Zeit_faellt_heraus()
|
||||
{
|
||||
Assert.That(Bildabtaster.Lesen("lavfi.blackframe.pblack=84\n", 0), Is.Empty);
|
||||
Assert.That(Frameabtaster.Lesen("lavfi.blackframe.pblack=84\n", 0), Is.Empty);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Leere_Ausgabe_ergibt_nichts()
|
||||
{
|
||||
Assert.That(Bildabtaster.Lesen("", 0), Is.Empty);
|
||||
Assert.That(Frameabtaster.Lesen("", 0), Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class BildabtasterLesenTests
|
||||
/// rotgemeldet — ein fehlendes Werkzeug ist kein Fehler im Quelltext.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class BildabtasterLaufTests
|
||||
public class FrameabtasterLaufTests
|
||||
{
|
||||
private string _ordner = "";
|
||||
private string _datei = "";
|
||||
@@ -117,8 +117,8 @@ public class BildabtasterLaufTests
|
||||
try { Directory.Delete(_ordner, recursive: true); } catch (IOException) { }
|
||||
}
|
||||
|
||||
private static Bildabtaster Abtaster() =>
|
||||
new(FfmpegOptions.FromEnvironment(), NullLogger<Bildabtaster>.Instance);
|
||||
private static Frameabtaster Abtaster() =>
|
||||
new(FfmpegOptions.FromEnvironment(), NullLogger<Frameabtaster>.Instance);
|
||||
|
||||
/// <summary>Die Kante liegt da, wo sie erzeugt wurde: bei 90 Sekunden.</summary>
|
||||
[Test]
|
||||
@@ -198,7 +198,7 @@ public class BildabtasterLaufTests
|
||||
/// 450 gewesen.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class BildabtasterBittiefeTests
|
||||
public class FrameabtasterBittiefeTests
|
||||
{
|
||||
private string _ordner = "";
|
||||
|
||||
@@ -246,8 +246,8 @@ public class BildabtasterBittiefeTests
|
||||
[Test]
|
||||
public async Task Acht_und_zehn_Bit_ergeben_dieselbe_Zahl()
|
||||
{
|
||||
var abtaster = new Bildabtaster(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Bildabtaster>.Instance);
|
||||
var abtaster = new Frameabtaster(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Frameabtaster>.Instance);
|
||||
|
||||
var acht = await abtaster.DunkelanteilAsync(Erzeugen("yuv420p"), bei: 2);
|
||||
var zehn = await abtaster.DunkelanteilAsync(Erzeugen("yuv420p10le"), bei: 2);
|
||||
@@ -241,7 +241,7 @@ public class HlsArgumenteTests
|
||||
|
||||
/// <summary>Beim Rechnen wird jedes Segment gleich lang.</summary>
|
||||
[Test]
|
||||
public void Beim_Rechnen_stehen_die_Schluesselbilder_fest()
|
||||
public void Beim_Rechnen_stehen_die_Keyframes_fest()
|
||||
{
|
||||
var zeile = Zeile([Bild("hevc"), Ton(1)]);
|
||||
|
||||
|
||||
@@ -197,15 +197,15 @@ public class HlsLaufTests
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Das ist der Test, der gefehlt hat.</b> Die Quelle hat genau ein
|
||||
/// Schlüsselbild, bei null; jeder Sprung landet also mitten in der
|
||||
/// Keyframe, bei null; jeder Sprung landet also mitten in der
|
||||
/// Bildgruppe — der schlimmste Fall. Genau gesprungen schnitte ffmpeg den
|
||||
/// Ton auf die Sekunde, das kopierte Bild aber nicht, und dazwischen läge
|
||||
/// Bild ohne Ton.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Zuerst hatte ich dafür den Sprung auf das Schlüsselbild gerundet. Das
|
||||
/// Zuerst hatte ich dafür den Sprung auf das Keyframe gerundet. Das
|
||||
/// war zu scharf geschliffen: eine Millisekunde zu klein, und ffmpeg nimmt
|
||||
/// das Schlüsselbild davor — gemessen wurden aus 0,004 s Abstand dann
|
||||
/// das Keyframe davor — gemessen wurden aus 0,004 s Abstand dann
|
||||
/// 4,98. `-noaccurate_seek` braucht diese Genauigkeit nicht.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
@@ -229,15 +229,15 @@ public class HlsLaufTests
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// <b>Die Quelle hat genau ein Schlüsselbild, bei null.</b> Ein Sprung auf
|
||||
/// <b>Die Quelle hat genau ein Keyframe, bei null.</b> Ein Sprung auf
|
||||
/// Sekunde 4 ist damit der schlimmste Fall: das kopierte Bild muss ganz
|
||||
/// vorn anfangen, der neu gerechnete Ton könnte bei 4 anfangen — vier
|
||||
/// Sekunden stummes Bild, und eine Uhr, die um vier Sekunden danebenliegt.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Gemessen an einer Quelle mit Schlüsselbildern alle fünf Sekunden und
|
||||
/// Gemessen an einer Quelle mit Keyframesn alle fünf Sekunden und
|
||||
/// einem Sprung auf Sekunde 12 war es genau so: Bild ab 0, Ton ab 1,98.
|
||||
/// Deshalb rundet der Koordinator den Sprung auf das Schlüsselbild —
|
||||
/// Deshalb rundet der Koordinator den Sprung auf das Keyframe —
|
||||
/// hier wird geprüft, dass die Zeile das auch verträgt.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
@@ -247,7 +247,7 @@ public class HlsLaufTests
|
||||
var quelle = Path.Combine(_ordner, "lang.mkv");
|
||||
Lang(quelle);
|
||||
|
||||
// Auf dem Schlüsselbild, so wie der Koordinator es rundet.
|
||||
// Auf dem Keyframe, so wie der Koordinator es rundet.
|
||||
var (bild, ton) = Anfang(Laufen(quelle, ab: 0));
|
||||
|
||||
Assert.That(Math.Abs(bild - ton), Is.LessThan(0.2),
|
||||
|
||||
+6
-6
@@ -5,7 +5,7 @@ using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace Flaro.Media.Tests;
|
||||
|
||||
/// <summary>Sagt die Schlüsselbildsuche dasselbe wie ffmpeg?</summary>
|
||||
/// <summary>Sagt die Keyframesuche dasselbe wie ffmpeg?</summary>
|
||||
///
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
@@ -24,7 +24,7 @@ namespace Flaro.Media.Tests;
|
||||
/// bleibt.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class SchluesselbilderTests
|
||||
public class KeyframesTests
|
||||
{
|
||||
private string _ordner = "";
|
||||
private string _datei = "";
|
||||
@@ -83,8 +83,8 @@ public class SchluesselbilderTests
|
||||
[TestCase(100.2)]
|
||||
public async Task Die_Suche_trifft_den_Anfang_des_Stroms(double wunsch)
|
||||
{
|
||||
var suche = new Schluesselbilder(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Schluesselbilder>.Instance);
|
||||
var suche = new Keyframes(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Keyframes>.Instance);
|
||||
|
||||
var gemeldet = await suche.DavorAsync(_datei, wunsch);
|
||||
var echt = EchterAnfang(wunsch);
|
||||
@@ -102,8 +102,8 @@ public class SchluesselbilderTests
|
||||
[TestCase(73.3)]
|
||||
public async Task Nie_hinter_dem_Wunsch(double wunsch)
|
||||
{
|
||||
var suche = new Schluesselbilder(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Schluesselbilder>.Instance);
|
||||
var suche = new Keyframes(
|
||||
FfmpegOptions.FromEnvironment(), NullLogger<Keyframes>.Instance);
|
||||
|
||||
Assert.That(await suche.DavorAsync(_datei, wunsch), Is.LessThanOrEqualTo(wunsch));
|
||||
}
|
||||
Reference in New Issue
Block a user