Dropbox-Podcast, selbstgebaut

Podcast-LogoIch bin leidenschaftlicher Podcast-Hörer. Es gibt nichts besseres, als bei Tätigkeiten, die wenig Aufmerksamkeit erfordern (Putzen, Kochen, Fahrradfahren, Basteln, …), nebenbei interessante Sachen zu hören. Dazu habe ich diverse fertige Podcasts abonniert. Allerdings kommt es immer wieder vor, dass mir außerhalb meiner abonnierten Podcasts einzelne Vorträge oder Sendungen über den Weg laufen, die ich ebenfalls gerne über meine Podcast-App hören würde. Denn die App (ich nutze Podkicker Pro) hat gewisse Vorteile: Ich kann meine Standard-App nutzen und diese auch mit meiner Pebble steuern, ich kann es ggf. beschleunigt hören, die Datei wird automatisch auf das Smartphone geladen und nach dem Hören von selbigem wieder gelöscht usw.

Um also einzelne MP3s automatisch in meinen Podcast-Workflow zu bekommen musste ein eigener Podcast her. Der sollte natürlich möglichst einfach von diversen Geräten aus gefüttert werden können. Ideal wäre es – dachte ich mir so – wenn ich einfach die Dateien in einen Dropbox-Ordner legen könnte und sie von dort aus alleine in meine Podcast-App wandern würden. Manchmal ist es auch nett, wenn man gar nicht das MP3 an sich in die Dropbox legen muss, sondern einfach eine URL-Datei, die auf das MP3 verweist. Meistens liegt das Ding ja schon irgendwo im Netz.

Und sowas hab ich mir dann gebastelt.

Bauanleitung

 

Im Wesentlichen braucht man zwei Dinge: Erstens muss man sich eine Dropbox-App anlegen, und zweitens muss man mein kleines PHP-Skript irgendwo auf einen Webserver legen.

1) Die Dropbox-App

Gehe auf https://www.dropbox.com/developers/apps und registriere dir eine App, indem du ober rechtsauf „Create App“ klickst. Dann noch ein paar Sachen einstellen und fertig:

Dropbox-App

 

Kleiner Hinweis: Früher konnte man App so anlgen, dass sie z.B. nur auf Audio-Files Zugriff hatte (siehe https://www.dropbox.com/developers-v1/reference/devguide). Das scheint jetzt nicht mehr zu gehen. Andere Infos irgend jemand?

Nach der Erstellung der App bekommt man einen „App key“ und ein „App secret“, beides sollte man sich notieren.

2) Das PHP-Skript

Hier brauchst du einen Webserver, auf dem du das PHP-Skript laufen lassen kannst. Ach ja, und das Skript benötigt das Dropbox-PHP-SDK, das man sich hier herunterladen kann: https://github.com/dropbox/dropbox-sdk-php. Beides, das SDK und das Skript, irgendwo auf den Webserver legen, ggf. noch per .htaccess schützen (Podkicker Pro unterstützt sowas z.B.) und einfach ausführen. Alles weitere, also die Authentifizierung der App etc. sagt einem dann das Skript. Nicht elegant, aber man sollte durchkommen.

So schaut das Skript aus, natürlich kannst du es auch einfach hier herunterladen:


<?php

// Hier kann man den Debugmodus setzen:
define("DEBUGMODUS", FALSE);





// // Diese Variablen mussen sukzessive gefüllt werden:

	// Diese beiden Variablen geben Zugriff auf die App, bekommt man nach deren Registrierung:
	$dropboxKey 	= '';
	$dropboxSecret 	= '';
	
	// Diese AuthCode bekommt man, nachdem man der App bei Dropbox Zugriff gegeben hat:
	$authCode 		= '';

	// Diese beiden Werte bekommt man, sobald man den AuthCode eingetragen hat:
	$accessToken 	= '';
	$dropboxUserId 	= '';

	// Welcher Dropbox-Ordner soll für den Podcast verwendet werden?
	$PodcastFolder = '/Podcast';









// Diverse Hilfen / Hintergründe:
// 
// Dropbox-SDK: 								https://www.dropbox.com/developers/core/start/php
// Dropbox-PHP-SDK:								https://github.com/dropbox/dropbox-sdk-php
// Dokumentation zum PHP-SDK: 					http://dropbox.github.io/dropbox-sdk-php/api-docs/v1.1.x/
// 
// Zunächst muss man eine Dropbox-App anlegen: 	https://www.dropbox.com/developers/apps/



// Falls noch keine App angelegt ist:
if (!$dropboxKey || !$dropboxSecret) {

	echo "<pre>";
	echo 'Bitte zun&auml;chst eine Dropbox-App anlegen: <a href="https://www.dropbox.com/developers/apps/">https://www.dropbox.com/developers/apps/</a>\n\n';
	echo 'Dabei bekommt man einen "App key" und einen "App secret", beide m&uuml;ssen in das Skript eingef&uuml;gt werden.';
	echo "</pre>";
	exit();
}


// Dropbox-SDK einbinden:
require_once "dropbox-sdk-php-1.1.6/lib/Dropbox/autoload.php";
use \Dropbox as dbx;
$appInfo = dbx\AppInfo::loadFromJson(array('key'=>$dropboxKey, 'secret'=>$dropboxSecret));
$webAuth = new dbx\WebAuthNoRedirect($appInfo, "PHP-Example/1.0");


// Bitte kein Cache:
header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
header("Cache-Control: post-check=0, pre-check=0", false);
header("Pragma: no-cache");



// Zunächst brauchen wir einen AuthCode nach der Authentifizierung der App:
if (!$authCode) {

	$authorizeUrl = $webAuth->start();

	echo "<pre>";
	echo "1. Gehe zu <a href='" . $authorizeUrl . "' target='_blank'>" . $authorizeUrl . "</a>\n";
	echo "2. Klick auf \"Erlauben\" (vielleicht musst du dich zuerst bei Dropbox einloggen).\n";
	echo "3. Kopieren den Auth-Code, den du dann von Dropbox erh&auml;ltst, und f&uuml;ge ihn in das PHP-Skript ein.\n";
	echo "</pre>";
	exit();
} 
// Hat man den Auth-Code, so muss man 1x daraus den Access-Token generieren:
elseif (!$accessToken || !$dropboxUserId) {

			list($accessToken, $dropboxUserId) = $webAuth->finish($authCode);

			echo "<pre>";
			print "Access Token:    " . $accessToken . "\n";
			print "Dropbox User ID: " . $dropboxUserId . "\n\n";
			print "Beides muss in das Skript eingetragen werden.";
			echo "</pre>";

			exit();
}







// Checken, ob Datein Remote eingebunden werden dürfen:
if(!ini_get('allow_url_fopen')) {
	echo "allow_url_fpoen ist deaktiviert. Lege eine php.ini in dem Verzeichnis auf dem Webserver an und schreibe 'allow_url_fopen = On;' hinein.";
	exit();
}


// Dropbox-Client erstellen:
$dbxClient = new dbx\Client($accessToken, "PHP-Example/1.0");




// MP3:
		# MP3-Dateien des Dropbox-Ordners auslesen:
		$PodcastFilesMP3 = $dbxClient->searchFileNames($PodcastFolder, '.mp3', null, false);
		// $PodcastFiles = $dbxClient->searchFileNames($PodcastFolder, '.mp', null, false);
		if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($PodcastFilesMP3); echo "</pre>";}


		# Liste der Dateien mit öffentlichen Links erzeugen:
		foreach ($PodcastFilesMP3 AS $key => $PodcastFile) {
			# Alles weitere nur dann machen, wenn das auch wirklich eine mp3-Datei ist:
			if ($PodcastFile['mime_type'] == 'audio/mpeg' && $PodcastFile['icon'] == 'page_white_sound' && $PodcastFile['is_dir'] == '') {
				# Öffentlichen Link erzeugen (siehe http://dropbox.github.io/dropbox-sdk-php/api-docs/v1.1.x/class-Dropbox.Client.html):
				$url = $dbxClient->createShareableLink($PodcastFile['path']);
				# Den Wert für den Download in der URL noch ersezten (?dl=0 => ?dl=1)
				$url = str_replace('?dl=0', '?dl=1', $url);
				# Werte dem Array zuweisen:
				$PodcastFilesMP3[$key]['url'] = $url;
			}
		}
		if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($PodcastFilesMP3); echo "</pre>";}






// URL:
		# URL-Dateien des Dropbox-Ordners auslesen:
		$PodcastFilesURL = $dbxClient->searchFileNames($PodcastFolder, '.url', null, false);
		if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($PodcastFilesURL); echo "</pre>";}

		# Liste der Dateien mit öffentlichen Links erzeugen:
		foreach ($PodcastFilesURL AS $key => $PodcastFile) {
			# Alles weitere nur dann machen, wenn das auch wirklich eine mp3-Datei ist:
			if ($PodcastFile['mime_type'] == 'text/url' && $PodcastFile['icon'] == 'page_white_linkfile' && $PodcastFile['is_dir'] == '') {
				# Öffentlichen Link aus der url-Datei auslesen:
				$url = $dbxClient->createShareableLink($PodcastFile['path']);
				# Den Wert für den Download in der URL noch ersezten (?dl=0 => ?dl=1)
				$url = str_replace('?dl=0', '?dl=1', $url);
				# Datei einlesen:
				if ($url) {
					$content = file($url, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
					if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($content); echo "</pre>";}
					# Die Zeile mit dem "URL=" suchen und den Wert auslesen:
					foreach($content AS $line) {
						if (substr($line, 0, 4) == 'URL=') {
							# Werte dem Array zuweisen:
							$PodcastFilesURL[$key]['url'] = substr($line, 4);
						}
					}
				}
			}
		}
		if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($PodcastFilesURL); echo "</pre>";}

// Die beiden Arrays zusammenpacken:
$PodcastFiles = array_merge($PodcastFilesMP3, $PodcastFilesURL);


// Header für xml senden: (Infos bei http://www.feedvalidator.org/)
if (!DEBUGMODUS)	{header("Content-type: text/xml; charset=utf-8");}


// Autor-Daten:
$author['name']		= 'DEIN NAME';
$author['mail']		= 'DEINE@MAILADRES.SE';
$author['www'] 		= 'http://DEINE.URL';
$author['size'] 	= 300;
// $author['avatar'] 	= 'http://www.gravatar.com/avatar/'.md5(strtolower(trim($author['mail']))).'?s='.$author['size'].'&d=monsterid'; # Bild von Gravatar holen (siehe https://de.gravatar.com/site/implement/images/php/)
// > das &-Zeichen in der URL macht wohl irgend ein Problem, also die Monster-ID mal rausgelassen...
$author['avatar'] 	= 'http://www.gravatar.com/avatar/'.md5(strtolower(trim($author['mail']))).'?s='.$author['size']; # Bild von Gravatar holen (siehe https://de.gravatar.com/site/implement/images/php/)
if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($author); echo "</pre><hr><img src='".$author['avatar']."'>";}


// Podcast-Daten:
$podcast['title'] 		= 'DEIN TITEL';
$podcast['generator'] 	= 'dasawebs Dropbox-Podcast-Generator';
$podcast['copyright'] 	= 'Alles zusammengeklautes Zeug, was soll ich sagen...';
$podcast['category'] 	= 'Miscellaneous';
if (DEBUGMODUS)	{echo "<hr><pre>"; print_r($podcast); echo "</pre>";}


$output .= '<?xml version="1.0" encoding="UTF-8"?>'; # Das sagt auch, dass Umlaute direkt im xml stehen dürfen
$output .= '<rss version="2.0">
			<channel>
				<title>'.$podcast['title'].'</title>
				<link>'.$author['www'].'</link>
				<description>'.$podcast['title'].'</description>
				<language>de-de</language>
				<copyright>'.$podcast['copyright'].'</copyright>
				<generator>'.$podcast['generator'].'</generator>
				<managingEditor>'.$author['name'].' ('.$author['mail'].')</managingEditor>
				<webMaster>'.$author['name'].' ('.$author['mail'].')</webMaster>
				<category>'.$podcast['category'].'</category>
				<lastBuildDate>'.timestamp_latest_file($PodcastFiles).'</lastBuildDate>
				<ttl>1440</ttl>
				<image>
					<url>'.$author['avatar'].'</url>
					<title>'.$podcast['title'].'</title>
					<link>'.$author['www'].'</link>
					<width>'.$author['size'].'</width>
					<height>'.$author['size'].'</height>
				</image>';



// Jetzt in Schleife die einzelnen Items ausgeben:
foreach ($PodcastFiles as $PodcastFile) {
	// Aber nur dann, wenn sie auch eine URL definiert bekommen haben:
	if ($PodcastFile['url']) $output .= '
				<item>
					<title>'.filename2title($PodcastFile['path']).'</title>
					<link>'.$PodcastFile['url'].'</link>
					<description>'.filename2title($PodcastFile['path']).'</description>
					<author>'.$author['name'].'</author>
					<pubDate>'.date("r", strtotime($PodcastFile['modified'])).'</pubDate>
					<guid>'.$PodcastFile['path'].'</guid>
					<source url="http://'.$_SERVER['HTTP_HOST'].'">'.$podcast['title'].'</source>
					<enclosure url="'.$PodcastFile[url].'" '.($PodcastFile['bytes']>200?'length="'.$PodcastFile['bytes'].'"':'').' type="audio/mpeg"/>
				</item>';
}

// <enclosure url="'.$PodcastFile[url].'" f="'.$PodcastFile['bytes'].'" type="audio/mpeg"/>


$output .= '
			</channel>
		</rss>';



// XML-Code zurückgeben:
if (DEBUGMODUS) {echo "<hr><pre>".htmlentities($output)."</pre>";}
else 			{echo $output;}
















function timestamp_latest_file($PodcastFiles) {
	// Array durchgehen und vergleichen, ob aktuelle Datei neuer ist als bisher neuste.
	// Dabei wird der Zeitstempel genommen, bei dem die Datei in die Dropbox geschrieben wurde,
	// nicht die, zu der das mp3 erzeugt wurde:
	foreach($PodcastFiles as $PodcastFile)
		if (!$lasttimestamp || $lasttimestamp < strtotime($PodcastFile['modified']))
				$lasttimestamp = strtotime($PodcastFile['modified']);

	# date('r') gibt genau den Zeitstempel RFC 2822-formatiert aus, wie es hier gebraucht wird:
	return date("r", $lasttimestamp);
}

function filename2title($filename) {
	// Funktion wandelt einen Dateinamen in einen Titel für den Podcast um.
	// D.h. im Wesentlichen: Dateiendung weg, Unterstriche ersetzen, ...

	$filename = basename($filename, '.mp3');
	$filename = basename($filename, '.url');
	$filename = str_replace('_', ' ', $filename);



	return $filename;
}



Viel Spaß damit!


Nachtrag

Mit der Zeit habe ich festgestellt, dass die Dropbox-API etwas zu langsam ist und daher einige Podcacher Probleme mit dem Podcast bekommen. Meine Lösung: Ich rufen den Podcast stündlich per Cronjob ab und speichere die Ausgabe in ein Cache-Verzeichnis:

wget --user="bla" --password="blubb" http://podcast.blablabla.de -O ~/podcast_blablabla_de/cache/index.html -o /dev/null

Als Podcast-URL verwende ich dann statt http://podcast.blablabla.de einfach http://podcast.blablabla.de/cache/ und fertig.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.