Die Micro-Blogplattform Tumblr nimmt Textbeiträge von Browsernutzern entgegen, akzeptiert über die API aber auch maschinell erstellte Artikel. Aus regelmäßig veröffentlichten Schnappschüssen einer Überwachungskamera entsteht so ein Blog der besonderen Art.
Durch mein Wohnzimmerfenster lugt seit einiger Zeit eine schwenkbare Kamera hinaus auf die Skyline San Franciscos. Sie lässt sich über das Internet fernsteuern und ihren Videostream kann ich aufs Handy holen, auch wenn ich gerade am anderen Ende der Welt weile. Das mit $60 relativ preiswerte Modell FI8910W mit Wifi-Anschluss von der Firma Foscam tut hier gute Dienste, denn es ist immer wieder beruhigend, auf Reisen übers Internet einen Blick aus dem Fenster daheim zu werfen und damit sicher zu stellen, dass die Wohnung noch nicht abgebrannt ist oder ausgeplündert wurde.
Abbildung 1: Die schwenkbare Foscam-Kamera im Wohnzimmer des Perlmeisters mit Blick auf Downtown San Francisco. |
Wer jetzt aber gleich zu Amazon hinüberschielt und mit einem Impulskauf liebäugelt, der sei jedoch gewarnt: Die preiswerte Foscam-Kamera hat eine nervige Macke: Sie schaltet die eingebaute Infrarotbeleuchtung ein, falls das Gerät sich nachts aufhängt und frisch bootet. Das kommt leider hin und wieder aus unerfindlichen Gründen vor, und falls es in der Nacht passiert, schaltet die Kamera mangels Umgebungslicht automatisch einen Ring von Infrarot-LEDs an. Direkt vor einem spiegelnden Fenster ist das freilich eine ausgesprochen dämliche Idee, denn ab dann besteht das Kamerabild aus imposanten Lichtmustern, und der zu überwachende Außenraum bleibt solange unüberwacht, bis jemand den Fehler bemerkt und den Infrarotring ausschaltet. Leider lässt sich diese Macke nicht abstellen und auf einen entsprechenden Firmware-Update haben die Kunden bislang vergebens gewartet. "You get what you pay for", sagt man dazu in Amerika.
Abbildung 2: Das altbackene Webinterface der Foscam-Kamera. |
Auch sieht das Web-Interface der Kamera aus wie ein Überbleibsel aus dem letzten Jahrhundert (Abbildung 2). Der User kann den Video-Stream mit ein paar Frames pro Sekunde verfolgen, und mit den Steuerknöpfen links oben im Bild den Kamerakopf um etwa 180 Grad seitwärts und auch etwa 90 Grad nach oben schwenken. Dass Foscam den HTTP-Zugriff mit dem Passwort im Klartext ausführt (kein HTTPS), ist freilich ein weiterer extremer Faux-Pas, und das sollten vernunftbegabte User auf dem offenen Internet nur über ein VPN machen. Steht die Kamera daheim und möchte man von außen darauf zugreifen, muss die Firewall des Routers mittels Port-Forwarding eingehende Requests weiterleiten. Da die Kamera mit einem dynamischen DNS-Provider zusammen arbeitet, erscheint das Web-Interface statisch unter einer URL wie http://xxx.myfoscam.org:5148, auch wenn der Internet-Provider die WAN-IP ändert.
Statt sich hin und wieder übers Internet in die Kamera einzuwählen und nach dem Rechten zu sehen, bietet es sich auch an, in regelmäßigen Zeitabständen Schnappschüsse von der Kamera abzuziehen und ins Internet zu stellen. Die Standbilder kann ein Skript dann auf einer Webseite aufreihen, und ein Blick genügt, um den Tagesverlauf zu kontrollieren. Neulich fiel mir auf, dass die vor etwa einem halben Jahr von meinem Arbeitgeber Yahoo geschluckte Micro-Blogplattform Tumblr eine programmierbare API anbietet, und ein Blick aufs CPAN offenbarte, dass ein eifriger Open-Source-Programmierer mit WWW::Tumblr bereits ein entsprechendes Perl-Modul veröffentlicht hatte.
Abbildung 3: Bei jedem Aufruf des Skripts tumblr-post erscheint im Tumblr-Blog ein Standbild der Kamera. |
Der Rest war Formsache. Ein Cronjob ruft das später in Listing 2 vorgestellte Skript mehrmals am Tag auf und holt einen aktuellen Schnappschuss von der Kamera ab. Das Bild schickt das Skript dann per API an das Tumblr-Blog, das die Meldungen chronologisch darstellt und allerhand sozialen Plattformschnickschnack wie "Like"-Knöpfe, oder eine "Reblog"-Funktion anbietet (Abbildung 3). Da User auf Tumblr üblicherweise mehrere Blogs verfolgen, bietet es sich an, den Bilderreigen in die Reihe interessierender Blogs aufzunehmen und neben anderen Postings zu konsumieren (Abbildung 4).
Abbildung 4: Nachdem das "Blog" in die Folgeliste aufgenommen wurde, erscheinen seine Postings zwischen anderen interessierenden Publikationen. |
Damit das später vorgestellte Perl-Skript tumblr-post
automatisch
schreibend auf das Blog eines Users zugreifen darf, muss Tumblr
es als Applikation registriert haben und der User muss ihr
entsprechende Rechte eingeräumt haben.
Auf [2] stehen die durchzuführenden Schritte
aufgelistet. Abbildung 5 zeigt die Registrierung der Testapplikation
"Perlsnapshot" und da es sich um ein Skript und nicht um eine App
fürs iPhone oder ein Android-Gerät handelt, habe ich die Felder
"App Store URL" und "Google Play Store URL" leer gelassen.
Abbildung 5: Nach der Registrierung der Applikation ... |
Abbildung 6: ... mit Angabe der Callback-URL ... |
Als "Default Callback URL", also die Web-Adresse, die Tumblr nach der Autorisierung seitens des Users mit den generierten Access-Tokens aufruft, ist "http://localhost:8082" eingetragen. Als Applikations-Icon habe ich einfach ein 128x128 Pixel großes Selbstportrait hochgeladen. Das später aufgerufene Skript in Listing 1 zum Einholen der Genemigung des Users und dem Einsammeln der Zugriffsschlüssel startet nämlich einen Webserver auf dem heimischen Linux-Rechner. Nach einem Klick auf "Register" unter dem ausgefüllten Formular zeigt Tumblr einen "Consumer Key" und einen "Secret Key" an (Abbildung 7). Diese beiden Hex-Strings identifizieren eine registrierte Tumblr-Applikation, die nun bei Usern um Zugriffsrechte buhlen darf.
Abbildung 7: ... generiert die Tumblr-API-Seite den Token. |
Wie schon in früheren Perlsnapshots mit anderen OAuth-kontrollierten Applikationen wie Google Drive ([3]) kommt auch diesmal zum Einholen der Erlaubnis zum Publizieren von Blog-Einträgen ein Perl-Skript mit dem Mojolicious-Modul vom CPAN zum Einsatz (Listing 1).
Es fährt beim Start ensprechend der Meldung
$ ./tumblr-oauth [Sat Oct 12 09:56:27 2013] [info] Listening at "http://localhost:8082". Server available at http://localhost:8082.
auf localhost
und auf Port 8082 einen Web-Server hoch, der, falls ein
Browser den URL http://localhost:8082 anfragt, nur einen Link mit
dem Text "Login on Tumblr" anzeigt. Klickt der User darauf, verzweigt der
Browser auf Tumblrs Login-Seite, wo der User sich mit Email und
Passwort identifizieren kann, falls er nicht schon eingeloggt ist.
Abbildung 8: Der User autorisiert das Skript zum Posten von neuen Tumblr-Artikeln. |
Tumblr zeigt dann den Dialog in Abbildung 8 an, um vom User die Erlaubnis
abzuholen, dass die Applikation "Perlsnapshot" sowohl lesend als
auch schreibend auf dessen Blog zugreifen darf. Das vorher von mir zu
Testzwecken eingerichtete Blog "Schtonk's Rants" unter schtonk.tumblr.com
soll später die Kamerabilder darstellen. Klickt der User auf "Allow",
verweist Tumblr den Browser zurück auf die vorher eingestellte Callback-URL
(http://localhost:8082) und schiebt dem Server dort mit oauth_token
und oauth_verifier
zwei weitere Zugriffstokens unter. Mit diesen
darf der Empfänger entsprechend den gewährten Rechten beliebig auf dem
Blog des Users herumorgeln, beinahe so, als wäre er selbst dort eingeloggt.
Allerdings musste der User der Applikation kein Passwort mitteilen, sondern
nur einen Token, den er jederzeit widerrufen kann.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use Storable; 04 use WWW::Tumblr::Authentication::OAuth; 05 use Mojolicious::Lite; 06 use YAML qw( DumpFile ); 07 use Sysadm::Install qw( :all ); 08 09 my( $home ) = glob "~"; 10 my $cfg_file = "$home/.tumblr.yml"; 11 my $local_url = "http://localhost:8082"; 12 my $consumer_key = "XXX"; 13 my $secret_key = "YYY"; 14 15 my $tumblr_oauth = 16 WWW::Tumblr::Authentication::OAuth->new( 17 consumer_key => $consumer_key, 18 secret_key => $secret_key, 19 callback => "$local_url/callback", 20 )->oauth_tools; 21 22 @ARGV = (qw(daemon --listen), $local_url); 23 24 ########################################### 25 get '/' => sub { 26 ########################################### 27 my( $self ) = @_; 28 29 $self->stash->{login_url} = 30 $tumblr_oauth->authorize_url(); 31 32 } => 'index'; 33 34 ########################################### 35 get '/callback' => sub { 36 ########################################### 37 my ( $self ) = @_; 38 39 my $oauth_token = 40 $self->param( "oauth_token" ); 41 my $oauth_verifier = 42 $self->param( "oauth_verifier" ); 43 44 $tumblr_oauth->oauth_token( 45 $oauth_token ); 46 $tumblr_oauth->oauth_verifier( 47 $oauth_verifier ); 48 49 DumpFile( $cfg_file, { 50 oauth_token => $oauth_token, 51 oauth_verifier => $oauth_verifier, 52 consumer_key => $consumer_key, 53 secret_key => $secret_key, 54 token => $tumblr_oauth->token(), 55 token_secret => 56 $tumblr_oauth->token_secret(), 57 } ); 58 59 $self->render( 60 text => "Tokens saved in $cfg_file.", 61 layout => 'default' ); 62 }; 63 64 app->start(); 65 66 __DATA__ 67 ########################################### 68 @@ index.html.ep 69 % layout 'default'; 70 <a href="<%= $login_url %>" 71 >Login on Tumblr</a> 72 73 @@ layouts/default.html.ep 74 <!doctype html><html> 75 <head><title>Token Fetcher</title></head> 76 <body> 77 <pre> 78 <%== content %> 79 </pre> 80 </body> 81 </html>
Listing 1 zieht hierzu das CPAN-Modul WWW::Tumblr::Authentication::OAuth
herein, das die notwendigen URLs zum Abholen der Tokens bereits enthält.
In den Zeilen 12 und 13 stehen in den Variablen $consumer_key
und
$secret_key
die in Abbildung 7 beim Registrieren der Applikation
erhaltenen Werte. Damit das Modul Mojolicious::Lite den Webserver
startet, erwartet es einige Optionen auf der Kommandozeile. Entgegen
allen Erwartungen startet die Option daemon
den Webserver im
Vordergrund, und der Parameter --listen
bekommt den in Zeile 11
gesetzten URL. Damit tumblr-oauth
ohne Kommandozeilenparameter
auskommt, stopft Zeile 22 die Optionen kurzerhand in den Array @ARGV
,
und gaukelt sie dem Mojolicious-Modul so vor.
Browseranfragen auf Port 8082 ohne Pfadangabe landen im Code in Listing
1 in Zeile 25,
die die Stash-Variable login_url
setzt und dann damit das Template im
DATA-Bereich des Skripts ab Zeile 68 in eine HTML-Seite mit einem
Login-Link umwandelt und den Browser darstellen lässt. Verzweigt Tumblr
nach dem Einholen der Erlaubnis seitens des Users zurück zum
Mojolicious-Server, geschieht dies unter dem Pfad /callback
und
der Code ab Zeile 35 kommt zur Ausführung.
Zeile 49 speichert mit DumpFile
aus dem CPAN-Modul YAML
alle
vier soweit erhaltenen Tokens sowie zwei neue mit token()
bzw.
token_secret()
erzeugte Access-Tokens im lesbaren YAML-Format
in der Datei .tumblr.yml
im Home-Verzeichnis ab. Später greifen
Applikationsskripts auf diese Datei zu, lesen die Tokens aus, und
erhalten dadurch Zugang zum Blog des Users.
01 #!/usr/local/bin/perl -w 02 use strict; 03 use Sysadm::Install qw(:all); 04 use YAML qw( LoadFile ); 05 use WWW::Tumblr; 06 use WWW::Mechanize; 07 08 my( $home ) = glob "~"; 09 my $cfg_file = "$home/.tumblr.yml"; 10 11 my $ua = WWW::Mechanize->new(); 12 my $picfile = "snapshot.jpg"; 13 my $url = "http://XXX.myfoscam.org" . 14 ":5148/snapshot.cgi"; 15 my $site = "schtonk.tumblr.com"; 16 my $cam_user = "XXX"; 17 my $cam_pass = "YYY"; 18 19 $ua->credentials( $cam_user, $cam_pass ); 20 21 my $resp = $ua->get( $url ); 22 blurt $resp->content(), $picfile; 23 24 my $cfg = LoadFile( $cfg_file ); 25 26 my $t = WWW::Tumblr->new( 27 consumer_key => $cfg->{ consumer_key }, 28 secret_key => $cfg->{ secret_key }, 29 token => $cfg->{ token }, 30 token_secret => $cfg->{ token_secret }, 31 ); 32 33 my $blog = $t->blog( $site ); 34 35 my $post = $blog->post( 36 type => 'photo', data => [$picfile] , 37 ); 38 39 if( $post ) { 40 print "I have published post id: " . 41 $post->{id}, "\n"; 42 } else { 43 die $blog->error; 44 }
Listing 2 zeigt die Anwendung, die die Fotos von der Überwachungskamera
abholt. Der URL in Zeile 13 ist an den Wert für die tatsächlich genutzte
Webadresse der Kamera anzupassen, sowie der Username und das Passwort für
die Weboberfläche der Kamera
in den Zeilen 16 und 17 einzutragen. Die Methode credentials
des
Objekts vom Typ WWW::Mechanize speichert diese Information und wird sie bei
Webanfragen automatisch beifügen, falls der Kamera-Webserver nach einem
Passwort fragt. Eigentlich genügte zum Einholen von Daten auf einem
Webserver das Perl-Modul LWP::UserAgent, aber die Superklasse
WWW::Mechanize bietet
eine bequemere credential()
-Methode an, die weniger Parameter
erfordert.
Die Foscom-Kamera liefert unter dem Pfad /snapshot.cgi
einen aktuellen Schnappschuss ihres Blickfelds und die Funktion blurt()
aus dem CPAN-Modul Sysadm::Install schreibt die Bilddaten in Zeile 22
in eine lokale Datei namens snapshot.jpg
. Nun kommen die Tumblr-Funktionen
zum Zug, Zeile 24 liest die YAML-Datei mit den Tokens ein und Zeile 26
erzeugt ein neues Objekt vom Typ WWW::Tumblr.
Mittels der Tumblr-API holt Zeile 33 dann Informationen zum vorher
definierten Testblog auf schtonk.tumblr.com
ein und die Methode
post()
sendet den Inhalt der Bilddatei unter Angabe des Typs
"photo"
an den Tumblr-Server. Im Erfolgsfall steht in $post
ein
wahrer Wert, und das Skript druckt die ID des neu angelegten Blogeintrags
aus:
$ ./tumblr-post I have published post id: 63791478637
Leider bietet die API noch keine Möglichkeit, zum Bild einen Text beizufügen, dies scheint zur Zeit nur durch das Browserinterface zu funktionieren, das richtige HTML-Seiten mit eingebauten Fotos erlaubt. Theoretisch könnte das Skript jedoch Texteinträge hochladen und die Bilder von einer anderen Seite referenzieren, doch für die Fotos der Überwachungskamera ist es ist praktischer, wenn Tumblr die Bilder selbst speichert. Eine weitere Möglichkeit wäre Beschriftung im Bild selbst, wie letzten Monat im Snapshot mittels des CPAN-Moduls Imager vorgestellt. So lassen sich kurze Texte wie zum Beispiel die aktuell gemessene Außentemperatur ins Bild einpassen.
Listings zu diesem Artikel: ftp://www.linux-magazin.de/pub/listings/magazin/2013/12/Perl
Tumblr API, http://www.tumblr.com/docs/en/api/v2
"Onlinebücherei", Michael Schilli, http://www.linux-magazin.de/Ausgaben/2013/02/Perl-Snapshot