WordPress als Podcast-Plattform

In den letzten Tagen bastelle ich an einer neuen Webseite, auf der ich einen Podcast aufbaue. Für diesen Auftritt waren übrigens auch die Kommentare auf der Indexseite gedacht. Was jetzt nur noch für einen richtigen Podcast gefehlt hat, war die Möglichkeit, den Podcast auch in einem sogenannten Podcatcher (also einem Programm zum Abrufen von Podcasts) zu abonnieren. DAS gestaltete sich jedoch schwieriger als zuerst angenommen.

Das erste Problem ist ein offizieller WordPress-Bug, der mit der Version 2.8 eingeführt worden war, inzwischen wieder gefixt wurde, aber anschließend wieder aufgenommen wurde. Dieser Fehler betrifft die benutzerdefinierten Felder, die man zu Artikeln und Seiten hinzufügen kann. Diese dienen dazu, zusätzliche Informationen zu Beiträgen abzuspeichern, damit diese z.B. von Plugins ausgewertet werden können.

Eines dieser benutzerdefinierten Felder, das direkt von WordPress verarbeitet wird, heißt "enclosure". Wie ich lernen durfte, wird mit Hilfe von Enclosures in einem Feed angegeben, dass zu einem Artikel noch zusätzliche Dateien gehören. Das ist bei Podcasts immens wichtig! Denn erst über diese Enclosures weiß ein Podcatcher, welche Audiodatei den Podcast darstellt.

Eigentlich bietet WordPress einem zwei Möglichkeiten, um solche Enclosures im Feed erzeugen zu lassen: Entweder, man verlinkt eine Mediendatei im Blogartikel, oder aber man legt dieses benutzerdefinierte Feld mit dem Namen "enclosure" und füllt es mit passenden Werten.
Irgendein WordPress-Entwickler kam nun aber irgendwann auf die Idee, das benutzerdefinierte Enclosure-Feld zu löschen, wenn die Datei nicht auch im Blogartikel verlinkt wurde - was, wohlgemerkt, die Wahlmöglichkeit ad absurdum führt und für viele Probleme gesorgt hat.

Da stand ich nun, fügte das Feld ein, füllte es mit Inhalten und musste mich wundern, dass es keine Wirkung hat. Man muss ja erstmal erkennen, dass die Einstellung immer wieder gelöscht wird! Als Ergebnis kam ich zur Erkenntnis, dass ich wohl oder übel die Mediendatei im Blogartikel verlinken muss. Wenn ihr mich fragt, ist das ziemlich hässlich, da ich den Link überhaupt nicht im Artikeltext haben will! Deshalb kam ich auf die Idee, den Link einfach dynamisch einfügen zu lassen.
Dafür habe ich den Filter "the_content_feed" gefunden, mit dem ich den Artikeltext bearbeiten kann, bevor er in den Feed verwurstelt wird. Das klappte soweit auch... außer, dass der dynamisch eingefügte Link nicht verarbeitet wurde. Tolle Scheiße! Danke WordPress! Für garnichts! Im Moment füge ich dank dieser Probleme den Link direkt in den Artikeltext ein, verstecke den Link via Inline-CSS.

Aber natürlich war das nicht das einzige Problem, das bei mir auftrat... um wenigstens herausfinden zu können, wie oft welche Datei heruntergeladen worden ist, habe ich ein kleines Script geschrieben, das einfach zählt, wie oft eine URL aufgerufen worden ist. Nach dem Zählen hat es dann einfach auf die eigentliche Datei weitergeleitet. Dieses Vorgehen sorgte jedoch bei WordPress dafür, dass es nicht mehr die Größe der eigentlichen Datei ermitteln konnte. Super!
Um dieses Problem zu umgehen und im Podcast-Feed die korrekte Dateigröße der Enclosures eintragen zu lassen, habe ich nun ein extra WordPress-Plugin geschrieben: "FixEnclosure". Dieses guckt nach, welche Datei als Enclosure eingetragen ist, sucht die tatsächliche Datei heraus, ermittelt die Größe und trägt diese dann in den Feed ein:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<?php
  /*
    Plugin Name: FixEnclosure
    Plugin URI: https://weizenspr.eu/
    Description: Fixes enclosure length.
    Version: 0.1c1
    Author: Kevin Niehage
    Author URI: https://weizenspr.eu
  */


  function fixenclosure_atom_execute($source) {
    // extract file name from link tag
    $fileName = basename(preg_replace("/\<(.+) href\="(.+)" (.+)\>/iU", "$2", $source));

    // get real file name
    $uploadDir = wp_upload_dir();
    $uploadDir = $uploadDir["basedir"] . "/";
    $fileName  = $uploadDir . $fileName;

    // get file size
    $duResult = exec("du -b " . escapeshellcmd($fileName));

    // set correct file size
    if (strlen($duResult) > 0) {
      $duResult = explode("\t", $duResult);
      $duResult = $duResult[0];

      $source = preg_replace("/\<(.+) length\="(.+)" (.+)\>/iU", "<$1 length="" . $duResult . "" $3>", $source);
    }

      return $source;
  }

  function fixenclosure_rss_execute($source) {
    // extract file name from enclosure tag
    $fileName = basename(preg_replace("/\<(.+) url\="(.+)" (.+)\>/iU", "$2", $source));

    // get real file name
    $uploadDir = wp_upload_dir();
    $uploadDir = $uploadDir["basedir"] . "/";
    $fileName  = $uploadDir . $fileName;

    // get file size
    $duResult = exec("du -b " . escapeshellcmd($fileName));

    // set correct file size
    if (strlen($duResult) > 0) {
      $duResult = explode("\t", $duResult);
      $duResult = $duResult[0];

      $source = preg_replace("/\<(.+) length\="(.+)" (.+)\>/iU", "<$1 length="" . $duResult . "" $3>", $source);
    }

      return $source;
  }

  add_filter('atom_enclosure', 'fixenclosure_atom_execute', 10, 1);
  add_filter('rss_enclosure',  'fixenclosure_rss_execute',  10, 1);
?>

Das ganze testete ich dann mit dem Online-Podcast-Validator von mirpod.com - und wow, es funktionierte 😀 ! Zumindest dachte ich das, denn am nächsten Morgen kamen die Beschwerden, dass man meinen Podcast nicht in iTunes abonnieren könne... mein Statistik-Script machte mir mal wieder einen Strich durch die Rechnung. Genauer gesagt: iTunes kommt nicht damit klar, dass sich hinter der URL ein Redirect versteckt. Also musste ich mein Script so anpassen, dass es zwar immernoch zählen kann, aber keine Weiterleitung mehr benutzt. Dabei herausgekommen, ist eine kleine Proxyfunktion:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
  function proxy_file_size($file) {
    $result = 0;

    $duResult = exec("du -b " . escapeshellcmd($file));
    if (strlen($duResult) > 0) {
      $duResult = explode("\t", $duResult);

      $result = trim($duResult[0]);
    }

    return $result;
  }

  function proxy_mime_type($file)
  {
    $result = "0";

    $fileResult = exec("/usr/bin/file -i -b " . escapeshellcmd(realpath($file)));
    if (strlen($fileResult) > 0) {
      $fileResult = explode(';', $fileResult);

      $result = trim($fileResult[0]);
    }

    return $result;
  }

  function proxy_file($file) {
    if (file_exists($file)) {
      $fileSize = proxy_file_size($file);

      if ($fileSize > 0) {
        header("Content-Type: " .  proxy_mime_type($file));
        header("Content-Length: " . $fileSize);

        readfile($file);
      }
    }
  }

Was ich aus dieser ganzen Odyssee gelernt habe, ist folgendes: Entweder du verwendest die vorhandene Software so, wie sie dir zur Verfügung steht (es sei denn, sie enthält einen Bug, der dich hindert, oder du musst eine gehörige Portion Wissen mitbringen und dich obendrein durch kryptische Google-Suchanfragen kämpfen, um dir das Wissen anzueignen, das du noch nicht besitzt.
Allein das Herausfinden, wie die Enclosures bei WordPress funktionieren und warum sie nicht so funktionieren, wie ich das möchte, hat schon eine halbe Ewigkeit gedauert. Empfehlen kann ich diese Arbeitsweise jedenfalls nur den wenigsten...

Zum Schluss noch etwas Rechtliches:
Der Autor dieses Programms haftet nicht für Schäden an Soft- oder Hardware oder Vermögensschäden, die durch das Benutzen des Programms entstehen, es sei denn, diese beruhen auf einem grob fahrlässigen oder vorsätzlichen Handeln des Autors, seiner Erfüllungsgehilfen oder seiner gesetzlichen Vertreter.
Podcastende Grüße, Kenny

Schreibe einen Kommentar

Um Ihnen beim weiteren Kommentieren auf dieser Webseite die erneute Eingabe Ihrer Daten zu ersparen, wird beim Absenden Ihres Kommentars ein Cookie an Ihren Browser gesendet und von diesem gespeichert. Mit dem Absenden eines Kommentars auf dieser Webseite stimmen Sie der Speicherung und Übertragung dieses Cookies explizit zu.

Pflichtfelder sind mit * markiert.