Artikel

Hörbücher auf der eigenen Seite: ein Seitenprojekt

Ein Abspieler mit Kapiteln, gespeicherter Position und Fortsetzen über Geräte hinweg. Klingt nach einem Nachmittag und hat mich einiges über Medienauslieferung gelehrt.

BlackZackBlackzack

915 Wörter · 5 Min. Lesezeit

  • web
  • medien
  • persoenlich

Dieses Projekt hat mit meinen übrigen wenig zu tun, und genau deshalb schreibe ich darüber. Ich wollte einen Bereich auf meiner Seite, in dem längere Audioaufnahmen mit Kapiteln liegen — ein einfacher Hörbuch-Abspieler.

Der Anlass war die Fantasy-Geschichte, an der ich seit dem Vorjahr schreibe. Der Gedanke, Kapitel auch zum Hören anzubieten, hat mich in ein Thema geführt, mit dem ich vorher nichts zu tun hatte.

Was ich unterschätzt habe

Audiodateien sind groß. Eine Stunde in vernünftiger Qualität liegt bei mehreren Dutzend Megabyte. Das ist nichts für den normalen Auslieferungsweg einer Webanwendung — es gehört an einen Ort, der für große Dateien gebaut ist, mit Bereichsanfragen.

Bereichsanfragen sind die Grundlage von allem. Wenn man in einer Audiodatei springt, lädt der Browser nicht die ganze Datei. Er fragt einen Bereich an, und der Server muss mit dem passenden Statuscode und den richtigen Kopfzeilen antworten. Fehlt diese Unterstützung, kann man nicht vorspulen, ohne alles zu laden — und das merkt man erst, wenn man es ausprobiert.

Kapitel sind kein Standard. Es gibt Formate mit eingebetteten Kapitelmarken, aber der Browser kann damit nichts anfangen. Also braucht es eine eigene Liste mit Zeitmarken.

{
  "id": "aeloria",
  "titel": "Der Nebel über Aeloria",
  "kapitel": [
    { "id": "k1", "titel": "Kapitel 1", "datei": "k1.m4a", "dauer": 1840 },
    { "id": "k2", "titel": "Kapitel 2", "datei": "k2.m4a", "dauer": 2010 }
  ]
}

Ich habe mich für einzelne Dateien pro Kapitel entschieden statt einer großen mit Marken. Der Grund ist praktisch: Ein Kapitel neu aufnehmen bedeutet dann, eine Datei zu ersetzen, statt alles neu zu schneiden.

Die Fortsetzungsposition

Der Teil, der aus einem Abspieler etwas Brauchbares macht: Man hört auf und will später weitermachen, wo man war.

Zwei Ebenen:

Lokal, damit es ohne Anmeldung funktioniert. Position im Browser gespeichert, alle paar Sekunden aktualisiert.

Serverseitig, für Angemeldete, damit man auf einem anderen Gerät weiterhören kann.

CREATE TABLE hoerposition (
  nutzer     VARCHAR(40) NOT NULL,
  buch       VARCHAR(40) NOT NULL,
  kapitel    VARCHAR(40) NOT NULL,
  sekunde    INT NOT NULL,
  aktualisiert DATETIME NOT NULL,
  PRIMARY KEY (nutzer, buch)
);

Die Falle dabei: Wie oft schreibt man? Bei jeder Sekunde sind das dreitausend Schreibvorgänge pro Stunde und Hörer. Ich schreibe alle fünfzehn Sekunden und zusätzlich bei Pause, Kapitelwechsel und beim Verlassen der Seite — mit einem Mechanismus, der auch dann noch sendet, wenn der Reiter geschlossen wird.

Infobox

Beim Weiterhören auf einem anderen Gerät habe ich eine Kleinigkeit eingebaut, die einen großen Unterschied macht: Es wird nicht exakt an der gespeicherten Sekunde fortgesetzt, sondern zehn Sekunden davor. Wer eine Pause gemacht hat, braucht einen kurzen Wiedereinstieg. Kommerzielle Abspieler machen das genauso, und mir ist es erst aufgefallen, als es sich ohne diesen Versatz falsch anfühlte.

Was ich über Medien im Browser gelernt habe

Automatisches Abspielen ist blockiert, und das ist richtig so. Jede Wiedergabe braucht eine Nutzerhandlung als Auslöser. Wer nach dem Laden automatisch startet, bekommt einen Fehler statt Ton.

Der Ladezustand ist mehrstufig. Es gibt Ereignisse für „genug Daten für den Anfang" und „genug Daten für alles". Wer nur auf eines davon hört, hat eine Anzeige, die entweder zu früh oder zu spät auf bereit springt.

Auf Mobilgeräten ist die Lautstärkeregelung des Abspielers wirkungslos. Die Lautstärke steuert das Gerät. Ein eigener Schieberegler, der nichts tut, ist schlechter als keiner.

Die Wiedergabe im Hintergrund verlangt Zusatzangaben. Damit auf dem Sperrbildschirm Titel und Kapitel stehen und die Systemtasten funktionieren, muss man die Metadaten ausdrücklich anmelden. Ohne das läuft der Ton weiter, aber die Bedienung fehlt.

Was ich über das Ausliefern großer Dateien gelernt habe

Der technisch lehrreichste Teil war nicht der Abspieler, sondern der Weg der Daten zum Browser.

Bereichsanfragen sind Pflicht, nicht Kür. Ohne sie kann der Browser nicht springen, ohne die ganze Datei zu laden. Ein Webserver kann das von sich aus; eine Anwendung, die Dateien selbst ausliefert, muss es ausdrücklich unterstützen — und die meisten einfachen Umsetzungen tun es nicht.

Der Zwischenspeicher entscheidet über die Kosten. Eine Audiodatei ändert sich nie. Mit einer langen Gültigkeit und einer unveränderlichen Adresse wird sie einmal geladen und danach nie wieder angefragt.

Die Datei gehört nicht durch die Anwendung. Wenn ein Anwendungsprozess mehrere Dutzend Megabyte ausliefert, blockiert er dabei Ressourcen, die er für seine eigentliche Aufgabe braucht. Das gehört an den Webserver davor.

Diese drei Punkte gelten für alles Große: Downloads, Bilder in Originalgröße, Archive, Pakete. Ich habe sie hier gelernt und ein Jahr später beim Ausliefern von Texturenpaketen für Spielserver direkt wieder gebraucht.

Warum Seitenprojekte ohne Zweck wichtig sind

Dieses Projekt hatte keinen Nutzerkreis und keinen Anlass. Und es war das erste seit langem, bei dem ich abends weitergemacht habe, ohne auf die Uhr zu sehen.

Ich glaube, das hat einen Grund, der über Spaß hinausgeht: Ein Projekt ohne Verpflichtung erlaubt Entscheidungen, die man sonst nicht trifft. Ich konnte Dinge ausprobieren, verwerfen, komplett neu bauen — ohne dass es jemanden gestört hätte.

In Systemen mit Nutzern geht das nicht. Dort ist jede Entscheidung mit der Frage verbunden, was sie für andere bedeutet, und das ist richtig so. Aber es begrenzt, wie viel man dabei ausprobiert.

Was ich daraus mitgenommen habe: Es lohnt sich, immer eine Sache zu haben, die niemand braucht. Nicht als Ausgleich, sondern weil man dort Dinge lernt, die man später an anderer Stelle einsetzt — ohne dass man beim Lernen unter Beobachtung stand.

Warum ich es trotzdem gebaut habe

Ehrlich gesagt: weil es Spaß gemacht hat. Es gibt keinen Nutzerkreis, der darauf gewartet hat, und es hat für kein Problem eine Lösung geliefert.

Was es hatte: ein klar umrissenes Ziel, ein sichtbares Ergebnis und ein Thema, mit dem ich nichts zu tun hatte. Nach einem Jahr, in dem ich überwiegend Sicherheitsdurchgänge, Datenbankumbauten und Verwaltungsschnittstellen gemacht habe, war das genau die richtige Abwechslung.

Und wie so oft ist etwas hängen geblieben, das später nützlich war — der Umgang mit großen Dateien, Bereichsanfragen und Zwischenspeicherung. Ein Jahr später habe ich Downloadpakete ausgeliefert und wusste, worauf zu achten ist.